var FIREFLYX;
(function (FIREFLYX) {
    /** 组件基本类 */
    class Component {
        constructor() {
            //是否被销毁
            this._isDisposed = false;
            this._instanceID = Component._instanceIDCount++;
        }
        /** 使用标记 */
        static get useSign() { return this._useSign; }
        get isDisposed() { return this._isDisposed; }
        //fucntion
        dispose() {
            this.entity = null;
            this._isDisposed = true;
        }
        getInstanceID() { return this._instanceID; }
    }
    Component._useSign = "none";
    /** 实例ID 记数*/
    Component._instanceIDCount = 0;
    FIREFLYX.Component = Component;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 复用的数组,适用于重复创建数组容器的场景,减少GC的消耗.
     * (实际上 arr.length = 0 , 不会分配新的内存，设计意义需要重新考虑了。)
     */
    class ReuseArray {
        /**
         * 复用数组
         * @param reallyClear 真的完全清理（效率更低）,建议存储基础值类型数据时关闭
         * @param items 初始数据
         */
        constructor(reallyClear = true, items) {
            this.buoy = -1;
            this.reallyClear = reallyClear;
            this.array = items != null ? items : [];
            this.buoy = this.array.length - 1;
        }
        /** 获取原始数组 对象 */
        getRawArray() { return this.array; }
        /** 当前使用长度 */
        get length() { return this.buoy + 1; }
        set length(val) { this.buoy = val - 1; }
        /**
         * 添加元素到有效的尾部()位置, length 增加1
         * @param val 值
         */
        push(val) {
            this.buoy++;
            this.array[this.buoy] = val;
        }
        /**
         * 获取指定索引的值
         * @param index 索引
         * @returns 值
         */
        get(index) {
            if (index > this.buoy)
                return null;
            return this.array[index];
        }
        /**
         * 设置指定索引的值
         * @param index 索引
         * @param val 值
         */
        set(index, val) {
            if (index > this.buoy)
                return;
            this.array[index] = val;
        }
        /** 数组所有值置为null  */
        clear() {
            const len = this.array.length;
            if (this.reallyClear) {
                for (let i = 0; i < len; i++) {
                    if (this.array[i] == null && i >= this.buoy)
                        break;
                    this.array[i] = null;
                }
            }
            this.buoy = -1;
        }
        /**
         * 克隆一个数组
         * @returns 克隆的数组对象
         */
        clone() {
            const result = new ReuseArray();
            if (this.length != 0) {
                result.array = this.array.concat();
                result.length = result.array.length = this.length;
            }
            return result;
        }
        /**
         * 从一个数组复制数据
         */
        copyFrom(arr) {
            if (!arr || arr == this)
                return;
            this.clear();
            const len = arr.length;
            const rawArr = arr.array;
            const myRawArr = this.array;
            for (let i = 0; i < len; i++) {
                myRawArr[i] = rawArr[i];
            }
            this.length = len;
        }
        /**
         * 就地排序数组。
         * @param compareFun 用于确定元素顺序的函数。
         * @returns 本数组
         */
        sort(compareFun) {
            const cFun = compareFun == null ? (a, b) => { return a - b; } : compareFun;
            const _fun = (a, b) => {
                const val = cFun(a, b);
                if (isNaN(val) || val == null)
                    return 0;
                return val;
            };
            const sort = (arr, left = 0, right = arr.length - 1) => {
                if (left >= right) { //如果左边的索引大于等于右边的索引说明整理完毕
                    return;
                }
                let i = left;
                let j = right;
                const baseVal = arr.get(j); // 取无序数组最后一个数为基准值
                while (i < j) { //把所有比基准值小的数放在左边大的数放在右边
                    while (i < j && _fun(arr.get(i), baseVal) <= 0) { //找到一个比基准值大的数交换
                        i++;
                    }
                    arr.set(j, arr.get(i)); // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己（i 等于 j）
                    while (j > i && _fun(baseVal, arr.get(j)) <= 0) { //找到一个比基准值小的数交换
                        j--;
                    }
                    arr.set(i, arr.get(j)); // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己（i 等于 j）
                }
                arr.set(j, baseVal); // 将基准值放至中央位置完成一次循环（这时候 j 等于 i ）
                sort(arr, left, j - 1); // 将左边的无序数组重复上面的操作
                sort(arr, j + 1, right); // 将右边的无序数组重复上面的操作
            };
            sort(this);
            return this;
        }
    }
    FIREFLYX.ReuseArray = ReuseArray;
    /** 对象池 */
    class Pool {
        /**
         * 初始化 对象池
         * @param type 池元素类型
         * @param initSize 初始化尺寸
         * @param onDelete 当delete时触发函数
         * @param extendable 是否可以拓展
         * @returns
         */
        constructor(type, initSize = 32, onDelete = null, extendable = true) {
            this._onDelete = null;
            if (!type) {
                console.warn(`type is null`);
                return;
            }
            this._onDelete = onDelete;
            this._poolList = [];
            this._extendable = extendable;
            this._type = type;
            this._size = 0;
            if (initSize > 0) {
                this.extend(initSize);
            }
        }
        /**
         * 拓展池大小
         * @param count 拓展大小
         */
        extend(count) {
            if (count < 2)
                count = 2;
            const t = this._type;
            for (let i = 0; i < count; i++) {
                const obj = new t();
                obj[Pool.inPoolTag] = true;
                this._poolList.push(new t());
            }
            this._size += count;
        }
        /** 池是否可以拓展 */
        get extendable() { return this._extendable; }
        /** 池中元素数量 */
        get size() { return this._size; }
        /**
         * 获取一个对象
         * @returns T 类型的对象
         */
        new() {
            let result = this._poolList.pop();
            if (!result) {
                if (!this._extendable)
                    return null;
                this.extend(this._size);
                result = this._poolList.pop();
            }
            delete result[Pool.inPoolTag];
            return result;
        }
        /**
         * 回收一个对象
         * @param obj T 类型的对象
         */
        delete(obj) {
            if (!obj)
                return;
            if (obj[Pool.inPoolTag]) {
                console.warn(`obj It's already in the pool`);
                return;
            }
            obj[Pool.inPoolTag] = true;
            this._poolList.push(obj);
            if (this._onDelete)
                this._onDelete(obj);
        }
    }
    Pool.inPoolTag = "__INPOOL__";
    FIREFLYX.Pool = Pool;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 场景游戏对象（本身没有特定逻辑，承载组件） */
    class GameObject {
        /**
         * 场景节点对象构造
         * @param name 节点名
         */
        constructor(name = "") {
            //private field
            //被销毁？
            this._isDisposed = false;
            //自身激活状态
            this._activeSelf = true;
            //在层级结构中的激活状态
            this._activeInHierarchy = true;
            //相同的组件只能加一个
            this._compsMap = new Map();
            //灯光 类型名
            this._lightTypeName = "";
            //渲染器 类型名
            this._rendererTypeName = "";
            //相机 类型名
            this._cameraTypeName = "";
            this._instanceID = GameObject._instanceIDCount++;
            GameObject._GameObjectMap.set(this._instanceID, this);
            this.name = name;
            this.addComponent(FIREFLYX.Transform);
            //创建回调
            if (GameObject._onGameObjectMake)
                GameObject._onGameObjectMake(this);
        }
        get isDisposed() { return this._isDisposed; }
        //get set
        /** 通过 entity实例ID 获取实体  */
        static getGameObject(entityInstID) {
            return this._GameObjectMap.get(entityInstID);
        }
        /** 组件数量 */
        get componentCount() { return this._compsMap.size; }
        /** 自己是激活状态 */
        get activeSelf() { return this._activeSelf; }
        /** 在节点层级中的激活状态 */
        get activeInHierarchy() { return this._activeInHierarchy; }
        /** 获取transform组件 */
        get transform() { return this._compsMap.get(FIREFLYX.Transform.name); }
        /** 尝试获取灯光组件 */
        get light() { return this._compsMap.get(this._lightTypeName); }
        /** 尝试获取渲染组件 */
        get renderer() { return this._compsMap.get(this._rendererTypeName); }
        /** 尝试获取相机组件 */
        get camera() { return this._compsMap.get(this._cameraTypeName); }
        //fucntions
        /**
         * 初始化
         * @param onGameObjectMake 当场景添加 实体
         * @param onGameObjectDispose 当场景销毁 实体
         * @param onGameObjectActiveInHChange 当场景实体在层级激活状态发生变化
         * @param onComponentAdd 当场景添加 组件
         * @param onComponentRemove 当场景移除 组件
         */
        static init(onGameObjectMake, onGameObjectDispose, onGameObjectActiveInHChange, onComponentAdd, onComponentRemove) {
            //记录函数
            this._onGameObjectMake = onGameObjectMake;
            this._onGameObjectDispose = onGameObjectDispose;
            this._onGameObjectActiveInHChange = onGameObjectActiveInHChange;
            this._onComponentAdd = onComponentAdd;
            this._onComponentRemove = onComponentRemove;
        }
        getInstanceID() { return this._instanceID; }
        /**
         * 设置 实体激活状态
         * @param active 是否激活
         */
        setActive(active) {
            this._activeSelf = active;
            //处理 子层级
            this.setActiveInHChildren(active);
        }
        dispose() {
            if (this._compsMap) {
                let keys = Object.keys(this._compsMap);
                keys.forEach((val) => {
                    let comp = this._compsMap.get(val);
                    if (comp) {
                        comp.dispose();
                        this._compsMap.delete(val);
                    }
                });
            }
            this._compsMap = null;
            GameObject._GameObjectMap.delete(this._instanceID);
            //
            this._isDisposed = true;
            //销毁回调
            if (GameObject._onGameObjectDispose)
                GameObject._onGameObjectDispose(this);
        }
        /**
         * 添加组件
         * @param ctor 组件类
         * @returns 添加的组件
         */
        addComponent(ctor) {
            let compTypeName = ctor.name;
            if (compTypeName == FIREFLYX.Component.name) {
                console.warn(`Component "${compTypeName}" can't attach.`);
                return null;
            }
            if (this._compsMap.has(compTypeName)) {
                console.warn(`Component "${compTypeName}" attach fail , same Type Component can attach of only one.`);
                return null;
            }
            if (!this._compKeys)
                this._compKeys = [];
            this._compKeys.push(compTypeName);
            if (ctor.useSign != "none") {
                let needErr = true;
                switch (ctor.useSign) {
                    case "light":
                        if (!this._lightTypeName) {
                            this._lightTypeName = compTypeName;
                            needErr = false;
                        }
                        break;
                    case "renderer":
                        if (!this._rendererTypeName) {
                            this._rendererTypeName = compTypeName;
                            needErr = false;
                        }
                        break;
                    case "camera":
                        if (!this._rendererTypeName) {
                            this._rendererTypeName = compTypeName;
                            needErr = false;
                        }
                        break;
                    default:
                }
                if (needErr) {
                    console.warn(`Component "${compTypeName}" attach fail , same useSign Component can attach of only one.`);
                    return null;
                }
            }
            //实例化 组件
            let comp = new ctor();
            comp.entity = this;
            this._compsMap.set(compTypeName, comp);
            //组件添加回调
            if (GameObject._onComponentAdd)
                GameObject._onComponentAdd(comp);
            return comp;
        }
        removeComponent(val) {
            if (!val)
                return;
            const _t = val instanceof (FIREFLYX.Component) ? FIREFLYX.Component : val;
            if (!_t || !_t.name)
                return;
            const _comp = this._compsMap.get(_t.name);
            if (!_comp)
                return;
            //清理标记
            switch (_t.useSign) {
                case "light":
                    this._lightTypeName = "";
                    break;
                case "renderer":
                    this._rendererTypeName = "";
                    break;
                case "camera":
                    this._cameraTypeName = "";
                    break;
                default:
            }
            this._compsMap.delete(_t.name);
            if (GameObject._onComponentRemove)
                GameObject._onComponentRemove(_comp);
        }
        /** 移除所有组件 */
        removeAllComponent() {
            const _map = this._compsMap;
            if (_map.size < 1)
                return;
            //
            this._lightTypeName = "";
            this._rendererTypeName = "";
            this._cameraTypeName = "";
            //
            let values = null;
            if (GameObject._onComponentRemove) {
                values = Object.values(_map);
            }
            //清理容器
            _map.clear();
            this._compKeys = null;
            //回调触发
            if (GameObject._onComponentRemove) {
                const vals = values;
                for (let i = 0, len = vals.length; i < len; i++) {
                    const _comp = vals[i];
                    if (!_comp)
                        continue;
                    GameObject._onComponentRemove(_comp);
                }
            }
        }
        getComponent(val) {
            if (val == null)
                return null;
            let compK = "";
            if (typeof (val) !== "number") {
                compK = val.name;
            }
            else {
                if (isNaN(val) || !this._compKeys || val < 0 || val >= this._compKeys.length)
                    return null;
                compK = this._compKeys[val];
            }
            return this._compsMap.get(compK);
        }
        /**
         * 设置 所有层级子节点 实体激活状态
         * @param active 是否激活
         */
        setActiveInHChildren(active) {
            const isChange = this._activeInHierarchy == active;
            this._activeInHierarchy = active;
            if (isChange && GameObject._onGameObjectActiveInHChange) {
                //激活状态变化回调
                GameObject._onGameObjectActiveInHChange(this);
            }
            const trans = this.transform;
            const cCount = trans.childCount;
            //传导到子节点
            for (let i = 0; i < cCount; i++) {
                const c = trans.getChild(i);
                if (!c)
                    continue;
                const e = c.entity;
                if (!e)
                    continue;
                if (active == false) {
                    if (e._activeInHierarchy == false)
                        continue; //该子节点已经标记过了
                }
                else {
                    if (e._activeSelf == e._activeInHierarchy)
                        continue; //该子节点不需要处理
                }
                e.setActiveInHChildren(active);
            }
        }
    }
    //static
    /** GameObject 实例ID 记数*/
    GameObject._instanceIDCount = 0;
    /** 所有实体容器 */
    GameObject._GameObjectMap = new Map();
    /** 需要调"Start()"的节点在 GameObject.List 中索引的列表 */
    GameObject.StartList = [];
    /** 需要调"Enable()"的节点在 GameObject.List 中索引的列表 */
    GameObject.EnableList = [];
    /** 需要调"Update()"的节点在 GameObject.List 中索引的列表 */
    GameObject.UpdateList = [];
    FIREFLYX.GameObject = GameObject;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    //常数定义
    //函数接口名-----------------------
    /** 开始函数名 */
    FIREFLYX.I_START_FUN = "start";
    /** 更新函数名 */
    FIREFLYX.I_UPDATE_FUN = "update";
    /** 稍后更新函数名 */
    FIREFLYX.I_LATE_UPDATE_FUN = "lateUpdate";
    /** 后渲染到纹理函数名 */
    FIREFLYX.I_ON_RENDER_IMAGE_FUN = "onRenderImage";
    /** 内建uniform 变量 */
    class BuiltinUniform {
    }
    /** 相机深度纹理 */
    BuiltinUniform.CAMERA_DEPTH_TEXTURE = "u_cameraDepthTexture";
    FIREFLYX.BuiltinUniform = BuiltinUniform;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class Renderer extends FIREFLYX.Component {
        constructor() {
            super(...arguments);
            /** 该渲染器所有的材质实例 */
            this._materialGroupList = [[]];
            this._castShadows = true;
            this._receiveShadows = true;
            this._isVisible = false;
            this._enable = true;
        }
        /** 渲染材质组 , 组中每一个材质对应一次绘制，顺序依次绘制*/
        get materialGroup() {
            const list = this._materialGroupList;
            if (list.length < 1) {
                list[0] = [];
            }
            return list[0]; //返回 0 的 materials
        }
        /** 渲染材质组 列表 , 列表中每个组对应每个 primitive 的绘制, 例如： materialGroupList[0] 对应第一个primitive , [1] 第二...*/
        get materialGroupList() { return this._materialGroupList; }
        /**
         * 获取网格顶点数据 数量
         */
        getVertexDataCount() {
            return 0;
        }
        /**
         * 上传网格顶点数据和状态
         * @param index 数据索引
         * @param out 返回结果数据
         */
        uploadVertexData(index, out) {
            return;
        }
        /** 是否投射阴影 */
        get castShadows() { return this._castShadows; }
        set castShadows(val) { this._castShadows = val; }
        /** 是否接收阴影 */
        get receiveShadows() { return this._receiveShadows; }
        set receiveShadows(val) { this._receiveShadows = val; }
        /** 是否在相机中可见 */
        get isVisible() { return this._isVisible; }
        /** 是否是启用渲染 */
        get enabled() { return this._enable; }
        set enabled(val) { this._enable = val; }
        dispose() {
            super.dispose();
            this._materialGroupList.length = 0;
        }
    }
    Renderer._useSign = "renderer";
    FIREFLYX.Renderer = Renderer;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    FIREFLYX.toGammaSpace = 1 / 2.2;
    FIREFLYX.toLinearSpace = 2.2;
    FIREFLYX.epsilon = 0.001;
    /** 弧度转到角度 系数 */
    FIREFLYX.toDegree = 180 / Math.PI;
    /** 角度转到弧度 系数 */
    FIREFLYX.toRadian = Math.PI / 180;
    /**
     * Class used to hold a RBG color
     */
    class Color3 {
        /**
         * Creates a new Color3 object from red, green, blue values, all between 0 and 1
         * @param r defines the red component (between 0 and 1, default is 0)
         * @param g defines the green component (between 0 and 1, default is 0)
         * @param b defines the blue component (between 0 and 1, default is 0)
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(
        /**
         * Defines the red component (between 0 and 1, default is 0)
         */
        r = 0, 
        /**
         * Defines the green component (between 0 and 1, default is 0)
         */
        g = 0, 
        /**
         * Defines the blue component (between 0 and 1, default is 0)
         */
        b = 0) {
            this.r = r;
            this.g = g;
            this.b = b;
        }
        /**
         * Creates a string with the Color3 current values
         * @returns the string representation of the Color3 object
         */
        toString() {
            return "{R: " + this.r + " G:" + this.g + " B:" + this.b + "}";
        }
        /**
         * Returns the string "Color3"
         * @returns "Color3"
         */
        getClassName() {
            return "Color3";
        }
        /**
         * Compute the Color3 hash code
         * @returns an unique number that can be used to hash Color3 objects
         */
        getHashCode() {
            let hash = this.r || 0;
            hash = (hash * 397) ^ (this.g || 0);
            hash = (hash * 397) ^ (this.b || 0);
            return hash;
        }
        // Operators
        /**
         * Stores in the given array from the given starting index the red, green, blue values as successive elements
         * @param array defines the array where to store the r,g,b components
         * @param index defines an optional index in the target array to define where to start storing values
         * @returns the current Color3 object
         */
        toArray(array, index) {
            if (index === undefined) {
                index = 0;
            }
            array[index] = this.r;
            array[index + 1] = this.g;
            array[index + 2] = this.b;
            return this;
        }
        /**
         * Returns a new {BABYLON.Color4} object from the current Color3 and the given alpha
         * @param alpha defines the alpha component on the new {BABYLON.Color4} object (default is 1)
         * @returns a new {BABYLON.Color4} object
         */
        toColor4(alpha = 1) {
            return new Color4(this.r, this.g, this.b, alpha);
        }
        /**
         * Returns a new array populated with 3 numeric elements : red, green and blue values
         * @returns the new array
         */
        asArray() {
            let result = new Array();
            this.toArray(result, 0);
            return result;
        }
        /**
         * Returns the luminance value
         * @returns a float value
         */
        toLuminance() {
            return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
        }
        /**
         * Multiply each Color3 rgb values by the given Color3 rgb values in a new Color3 object
         * @param otherColor defines the second operand
         * @returns the new Color3 object
         */
        multiply(otherColor) {
            return new Color3(this.r * otherColor.r, this.g * otherColor.g, this.b * otherColor.b);
        }
        /**
         * Multiply the rgb values of the Color3 and the given Color3 and stores the result in the object "result"
         * @param otherColor defines the second operand
         * @param result defines the Color3 object where to store the result
         * @returns the current Color3
         */
        multiplyToRef(otherColor, result) {
            result.r = this.r * otherColor.r;
            result.g = this.g * otherColor.g;
            result.b = this.b * otherColor.b;
            return this;
        }
        /**
         * Determines equality between Color3 objects
         * @param otherColor defines the second operand
         * @returns true if the rgb values are equal to the given ones
         */
        equals(otherColor) {
            return otherColor && this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b;
        }
        /**
         * Determines equality between the current Color3 object and a set of r,b,g values
         * @param r defines the red component to check
         * @param g defines the green component to check
         * @param b defines the blue component to check
         * @returns true if the rgb values are equal to the given ones
         */
        equalsFloats(r, g, b) {
            return this.r === r && this.g === g && this.b === b;
        }
        /**
         * Multiplies in place each rgb value by scale
         * @param scale defines the scaling factor
         * @returns the updated Color3
         */
        scale(scale) {
            return new Color3(this.r * scale, this.g * scale, this.b * scale);
        }
        /**
         * Multiplies the rgb values by scale and stores the result into "result"
         * @param scale defines the scaling factor
         * @param result defines the Color3 object where to store the result
         * @returns the unmodified current Color3
         */
        scaleToRef(scale, result) {
            result.r = this.r * scale;
            result.g = this.g * scale;
            result.b = this.b * scale;
            return this;
        }
        /**
         * Scale the current Color3 values by a factor and add the result to a given Color3
         * @param scale defines the scale factor
         * @param result defines color to store the result into
         * @returns the unmodified current Color3
         */
        scaleAndAddToRef(scale, result) {
            result.r += this.r * scale;
            result.g += this.g * scale;
            result.b += this.b * scale;
            return this;
        }
        /**
         * Clamps the rgb values by the min and max values and stores the result into "result"
         * @param min defines minimum clamping value (default is 0)
         * @param max defines maximum clamping value (default is 1)
         * @param result defines color to store the result into
         * @returns the original Color3
         */
        clampToRef(min = 0, max = 1, result) {
            result.r = FIREFLYX.Scalar.Clamp(this.r, min, max);
            result.g = FIREFLYX.Scalar.Clamp(this.g, min, max);
            result.b = FIREFLYX.Scalar.Clamp(this.b, min, max);
            return this;
        }
        /**
         * Creates a new Color3 set with the added values of the current Color3 and of the given one
         * @param otherColor defines the second operand
         * @returns the new Color3
         */
        add(otherColor) {
            return new Color3(this.r + otherColor.r, this.g + otherColor.g, this.b + otherColor.b);
        }
        /**
         * Stores the result of the addition of the current Color3 and given one rgb values into "result"
         * @param otherColor defines the second operand
         * @param result defines Color3 object to store the result into
         * @returns the unmodified current Color3
         */
        addToRef(otherColor, result) {
            result.r = this.r + otherColor.r;
            result.g = this.g + otherColor.g;
            result.b = this.b + otherColor.b;
            return this;
        }
        /**
         * Returns a new Color3 set with the subtracted values of the given one from the current Color3
         * @param otherColor defines the second operand
         * @returns the new Color3
         */
        subtract(otherColor) {
            return new Color3(this.r - otherColor.r, this.g - otherColor.g, this.b - otherColor.b);
        }
        /**
         * Stores the result of the subtraction of given one from the current Color3 rgb values into "result"
         * @param otherColor defines the second operand
         * @param result defines Color3 object to store the result into
         * @returns the unmodified current Color3
         */
        subtractToRef(otherColor, result) {
            result.r = this.r - otherColor.r;
            result.g = this.g - otherColor.g;
            result.b = this.b - otherColor.b;
            return this;
        }
        /**
         * Copy the current object
         * @returns a new Color3 copied the current one
         */
        clone() {
            return new Color3(this.r, this.g, this.b);
        }
        /**
         * Copies the rgb values from the source in the current Color3
         * @param source defines the source Color3 object
         * @returns the updated Color3 object
         */
        copyFrom(source) {
            this.r = source.r;
            this.g = source.g;
            this.b = source.b;
            return this;
        }
        /**
         * Updates the Color3 rgb values from the given floats
         * @param r defines the red component to read from
         * @param g defines the green component to read from
         * @param b defines the blue component to read from
         * @returns the current Color3 object
         */
        copyFromFloats(r, g, b) {
            this.r = r;
            this.g = g;
            this.b = b;
            return this;
        }
        /**
         * Updates the Color3 rgb values from the given floats
         * @param r defines the red component to read from
         * @param g defines the green component to read from
         * @param b defines the blue component to read from
         * @returns the current Color3 object
         */
        set(r, g, b) {
            return this.copyFromFloats(r, g, b);
        }
        /**
         * Compute the Color3 hexadecimal code as a string
         * @returns a string containing the hexadecimal representation of the Color3 object
         */
        toHexString() {
            let intR = (this.r * 255) | 0;
            let intG = (this.g * 255) | 0;
            let intB = (this.b * 255) | 0;
            return "#" + FIREFLYX.Scalar.ToHex(intR) + FIREFLYX.Scalar.ToHex(intG) + FIREFLYX.Scalar.ToHex(intB);
        }
        /**
         * Computes a new Color3 converted from the current one to linear space
         * @returns a new Color3 object
         */
        toLinearSpace() {
            let convertedColor = new Color3();
            this.toLinearSpaceToRef(convertedColor);
            return convertedColor;
        }
        /**
         * Converts the Color3 values to linear space and stores the result in "convertedColor"
         * @param convertedColor defines the Color3 object where to store the linear space version
         * @returns the unmodified Color3
         */
        toLinearSpaceToRef(convertedColor) {
            convertedColor.r = Math.pow(this.r, FIREFLYX.toLinearSpace);
            convertedColor.g = Math.pow(this.g, FIREFLYX.toLinearSpace);
            convertedColor.b = Math.pow(this.b, FIREFLYX.toLinearSpace);
            return this;
        }
        /**
         * Computes a new Color3 converted from the current one to gamma space
         * @returns a new Color3 object
         */
        toGammaSpace() {
            let convertedColor = new Color3();
            this.toGammaSpaceToRef(convertedColor);
            return convertedColor;
        }
        /**
         * Converts the Color3 values to gamma space and stores the result in "convertedColor"
         * @param convertedColor defines the Color3 object where to store the gamma space version
         * @returns the unmodified Color3
         */
        toGammaSpaceToRef(convertedColor) {
            convertedColor.r = Math.pow(this.r, FIREFLYX.toGammaSpace);
            convertedColor.g = Math.pow(this.g, FIREFLYX.toGammaSpace);
            convertedColor.b = Math.pow(this.b, FIREFLYX.toGammaSpace);
            return this;
        }
        // Statics
        /**
         * Creates a new Color3 from the string containing valid hexadecimal values
         * @param hex defines a string containing valid hexadecimal values
         * @returns a new Color3 object
         */
        static FromHexString(hex) {
            if (hex.substring(0, 1) !== "#" || hex.length !== 7) {
                return new Color3(0, 0, 0);
            }
            let r = parseInt(hex.substring(1, 3), 16);
            let g = parseInt(hex.substring(3, 5), 16);
            let b = parseInt(hex.substring(5, 7), 16);
            return Color3.FromInts(r, g, b);
        }
        /**
         * Creates a new Vector3 from the starting index of the given array
         * @param array defines the source array
         * @param offset defines an offset in the source array
         * @returns a new Color3 object
         */
        static FromArray(array, offset = 0) {
            return new Color3(array[offset], array[offset + 1], array[offset + 2]);
        }
        /**
         * Creates a new Color3 from integer values (< 256)
         * @param r defines the red component to read from (value between 0 and 255)
         * @param g defines the green component to read from (value between 0 and 255)
         * @param b defines the blue component to read from (value between 0 and 255)
         * @returns a new Color3 object
         */
        static FromInts(r, g, b) {
            return new Color3(r / 255.0, g / 255.0, b / 255.0);
        }
        /**
         * Creates a new Color3 with values linearly interpolated of "amount" between the start Color3 and the end Color3
         * @param start defines the start Color3 value
         * @param end defines the end Color3 value
         * @param amount defines the gradient value between start and end
         * @returns a new Color3 object
         */
        static Lerp(start, end, amount) {
            let r = start.r + ((end.r - start.r) * amount);
            let g = start.g + ((end.g - start.g) * amount);
            let b = start.b + ((end.b - start.b) * amount);
            return new Color3(r, g, b);
        }
        /**
         * Returns a Color3 value containing a red color
         * @returns a new Color3 object
         */
        static Red() { return new Color3(1, 0, 0); }
        /**
         * Returns a Color3 value containing a green color
         * @returns a new Color3 object
         */
        static Green() { return new Color3(0, 1, 0); }
        /**
         * Returns a Color3 value containing a blue color
         * @returns a new Color3 object
         */
        static Blue() { return new Color3(0, 0, 1); }
        /**
         * Returns a Color3 value containing a black color
         * @returns a new Color3 object
         */
        static Black() { return new Color3(0, 0, 0); }
        /**
         * Returns a Color3 value containing a white color
         * @returns a new Color3 object
         */
        static White() { return new Color3(1, 1, 1); }
        /**
         * Returns a Color3 value containing a purple color
         * @returns a new Color3 object
         */
        static Purple() { return new Color3(0.5, 0, 0.5); }
        /**
         * Returns a Color3 value containing a magenta color
         * @returns a new Color3 object
         */
        static Magenta() { return new Color3(1, 0, 1); }
        /**
         * Returns a Color3 value containing a yellow color
         * @returns a new Color3 object
         */
        static Yellow() { return new Color3(1, 1, 0); }
        /**
         * Returns a Color3 value containing a gray color
         * @returns a new Color3 object
         */
        static Gray() { return new Color3(0.5, 0.5, 0.5); }
        /**
         * Returns a Color3 value containing a teal color
         * @returns a new Color3 object
         */
        static Teal() { return new Color3(0, 1.0, 1.0); }
        /**
         * Returns a Color3 value containing a random color
         * @returns a new Color3 object
         */
        static Random() { return new Color3(Math.random(), Math.random(), Math.random()); }
    }
    FIREFLYX.Color3 = Color3;
    /**
     * Class used to hold a RBGA color
     */
    class Color4 {
        /**
         * Creates a new Color4 object from red, green, blue values, all between 0 and 1
         * @param r defines the red component (between 0 and 1, default is 0)
         * @param g defines the green component (between 0 and 1, default is 0)
         * @param b defines the blue component (between 0 and 1, default is 0)
         * @param a defines the alpha component (between 0 and 1, default is 1)
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(
        /**
         * Defines the red component (between 0 and 1, default is 0)
         */
        r = 0, 
        /**
         * Defines the green component (between 0 and 1, default is 0)
         */
        g = 0, 
        /**
         * Defines the blue component (between 0 and 1, default is 0)
         */
        b = 0, 
        /**
         * Defines the alpha component (between 0 and 1, default is 1)
         */
        a = 1) {
            this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
        }
        // Operators
        /**
         * Adds in place the given Color4 values to the current Color4 object
         * @param right defines the second operand
         * @returns the current updated Color4 object
         */
        addInPlace(right) {
            this.r += right.r;
            this.g += right.g;
            this.b += right.b;
            this.a += right.a;
            return this;
        }
        /**
         * Creates a new array populated with 4 numeric elements : red, green, blue, alpha values
         * @returns the new array
         */
        asArray() {
            let result = new Array();
            this.toArray(result, 0);
            return result;
        }
        /**
         * Stores from the starting index in the given array the Color4 successive values
         * @param array defines the array where to store the r,g,b components
         * @param index defines an optional index in the target array to define where to start storing values
         * @returns the current Color4 object
         */
        toArray(array, index) {
            if (index === undefined) {
                index = 0;
            }
            array[index] = this.r;
            array[index + 1] = this.g;
            array[index + 2] = this.b;
            array[index + 3] = this.a;
            return this;
        }
        /**
         * Creates a new Color4 set with the added values of the current Color4 and of the given one
         * @param right defines the second operand
         * @returns a new Color4 object
         */
        add(right) {
            return new Color4(this.r + right.r, this.g + right.g, this.b + right.b, this.a + right.a);
        }
        /**
         * Creates a new Color4 set with the subtracted values of the given one from the current Color4
         * @param right defines the second operand
         * @returns a new Color4 object
         */
        subtract(right) {
            return new Color4(this.r - right.r, this.g - right.g, this.b - right.b, this.a - right.a);
        }
        /**
         * Subtracts the given ones from the current Color4 values and stores the results in "result"
         * @param right defines the second operand
         * @param result defines the Color4 object where to store the result
         * @returns the current Color4 object
         */
        subtractToRef(right, result) {
            result.r = this.r - right.r;
            result.g = this.g - right.g;
            result.b = this.b - right.b;
            result.a = this.a - right.a;
            return this;
        }
        /**
         * Creates a new Color4 with the current Color4 values multiplied by scale
         * @param scale defines the scaling factor to apply
         * @returns a new Color4 object
         */
        scale(scale) {
            return new Color4(this.r * scale, this.g * scale, this.b * scale, this.a * scale);
        }
        /**
         * Multiplies the current Color4 values by scale and stores the result in "result"
         * @param scale defines the scaling factor to apply
         * @param result defines the Color4 object where to store the result
         * @returns the current unmodified Color4
         */
        scaleToRef(scale, result) {
            result.r = this.r * scale;
            result.g = this.g * scale;
            result.b = this.b * scale;
            result.a = this.a * scale;
            return this;
        }
        /**
         * Scale the current Color4 values by a factor and add the result to a given Color4
         * @param scale defines the scale factor
         * @param result defines the Color4 object where to store the result
         * @returns the unmodified current Color4
         */
        scaleAndAddToRef(scale, result) {
            result.r += this.r * scale;
            result.g += this.g * scale;
            result.b += this.b * scale;
            result.a += this.a * scale;
            return this;
        }
        /**
         * Clamps the rgb values by the min and max values and stores the result into "result"
         * @param min defines minimum clamping value (default is 0)
         * @param max defines maximum clamping value (default is 1)
         * @param result defines color to store the result into.
         * @returns the cuurent Color4
         */
        clampToRef(min = 0, max = 1, result) {
            result.r = FIREFLYX.Scalar.Clamp(this.r, min, max);
            result.g = FIREFLYX.Scalar.Clamp(this.g, min, max);
            result.b = FIREFLYX.Scalar.Clamp(this.b, min, max);
            result.a = FIREFLYX.Scalar.Clamp(this.a, min, max);
            return this;
        }
        /**
          * Multipy an Color4 value by another and return a new Color4 object
          * @param color defines the Color4 value to multiply by
          * @returns a new Color4 object
          */
        multiply(color) {
            return new Color4(this.r * color.r, this.g * color.g, this.b * color.b, this.a * color.a);
        }
        /**
         * Multipy a Color4 value by another and push the result in a reference value
         * @param color defines the Color4 value to multiply by
         * @param result defines the Color4 to fill the result in
         * @returns the result Color4
         */
        multiplyToRef(color, result) {
            result.r = this.r * color.r;
            result.g = this.g * color.g;
            result.b = this.b * color.b;
            result.a = this.a * color.a;
            return result;
        }
        /**
         * Creates a string with the Color4 current values
         * @returns the string representation of the Color4 object
         */
        toString() {
            return "{R: " + this.r + " G:" + this.g + " B:" + this.b + " A:" + this.a + "}";
        }
        /**
         * Returns the string "Color4"
         * @returns "Color4"
         */
        getClassName() {
            return "Color4";
        }
        /**
         * Compute the Color4 hash code
         * @returns an unique number that can be used to hash Color4 objects
         */
        getHashCode() {
            let hash = this.r || 0;
            hash = (hash * 397) ^ (this.g || 0);
            hash = (hash * 397) ^ (this.b || 0);
            hash = (hash * 397) ^ (this.a || 0);
            return hash;
        }
        /**
         * Creates a new Color4 copied from the current one
         * @returns a new Color4 object
         */
        clone() {
            return new Color4(this.r, this.g, this.b, this.a);
        }
        /**
         * Copies the given Color4 values into the current one
         * @param source defines the source Color4 object
         * @returns the current updated Color4 object
         */
        copyFrom(source) {
            this.r = source.r;
            this.g = source.g;
            this.b = source.b;
            this.a = source.a;
            return this;
        }
        /**
         * Copies the given float values into the current one
         * @param r defines the red component to read from
         * @param g defines the green component to read from
         * @param b defines the blue component to read from
         * @param a defines the alpha component to read from
         * @returns the current updated Color4 object
         */
        copyFromFloats(r, g, b, a) {
            this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
            return this;
        }
        /**
         * Copies the given float values into the current one
         * @param r defines the red component to read from
         * @param g defines the green component to read from
         * @param b defines the blue component to read from
         * @param a defines the alpha component to read from
         * @returns the current updated Color4 object
         */
        set(r, g, b, a) {
            return this.copyFromFloats(r, g, b, a);
        }
        /**
         * Compute the Color4 hexadecimal code as a string
         * @returns a string containing the hexadecimal representation of the Color4 object
         */
        toHexString() {
            let intR = (this.r * 255) | 0;
            let intG = (this.g * 255) | 0;
            let intB = (this.b * 255) | 0;
            let intA = (this.a * 255) | 0;
            return "#" + FIREFLYX.Scalar.ToHex(intR) + FIREFLYX.Scalar.ToHex(intG) + FIREFLYX.Scalar.ToHex(intB) + FIREFLYX.Scalar.ToHex(intA);
        }
        /**
         * Computes a new Color4 converted from the current one to linear space
         * @returns a new Color4 object
         */
        toLinearSpace() {
            let convertedColor = new Color4();
            this.toLinearSpaceToRef(convertedColor);
            return convertedColor;
        }
        /**
         * Converts the Color4 values to linear space and stores the result in "convertedColor"
         * @param convertedColor defines the Color4 object where to store the linear space version
         * @returns the unmodified Color4
         */
        toLinearSpaceToRef(convertedColor) {
            convertedColor.r = Math.pow(this.r, FIREFLYX.toLinearSpace);
            convertedColor.g = Math.pow(this.g, FIREFLYX.toLinearSpace);
            convertedColor.b = Math.pow(this.b, FIREFLYX.toLinearSpace);
            convertedColor.a = this.a;
            return this;
        }
        /**
         * Computes a new Color4 converted from the current one to gamma space
         * @returns a new Color4 object
         */
        toGammaSpace() {
            let convertedColor = new Color4();
            this.toGammaSpaceToRef(convertedColor);
            return convertedColor;
        }
        /**
         * Converts the Color4 values to gamma space and stores the result in "convertedColor"
         * @param convertedColor defines the Color4 object where to store the gamma space version
         * @returns the unmodified Color4
         */
        toGammaSpaceToRef(convertedColor) {
            convertedColor.r = Math.pow(this.r, FIREFLYX.toGammaSpace);
            convertedColor.g = Math.pow(this.g, FIREFLYX.toGammaSpace);
            convertedColor.b = Math.pow(this.b, FIREFLYX.toGammaSpace);
            convertedColor.a = this.a;
            return this;
        }
        // Statics
        /**
         * Creates a new Color4 from the string containing valid hexadecimal values
         * @param hex defines a string containing valid hexadecimal values
         * @returns a new Color4 object
         */
        static FromHexString(hex) {
            if (hex.substring(0, 1) !== "#" || hex.length !== 9) {
                return new Color4(0.0, 0.0, 0.0, 0.0);
            }
            let r = parseInt(hex.substring(1, 3), 16);
            let g = parseInt(hex.substring(3, 5), 16);
            let b = parseInt(hex.substring(5, 7), 16);
            let a = parseInt(hex.substring(7, 9), 16);
            return Color4.FromInts(r, g, b, a);
        }
        /**
         * Creates a new Color4 object set with the linearly interpolated values of "amount" between the left Color4
         * object and the right Color4 object
         * @param left defines the start value
         * @param right defines the end value
         * @param amount defines the gradient factor
         * @returns a new Color4 object
         */
        static Lerp(left, right, amount) {
            let result = new Color4(0.0, 0.0, 0.0, 0.0);
            Color4.LerpToRef(left, right, amount, result);
            return result;
        }
        /**
         * Set the given "result" with the linearly interpolated values of "amount" between the left Color4 object and the right Color4 object
         * @param left defines the start value
         * @param right defines the end value
         * @param amount defines the gradient factor
         * @param result defines the Color4 object where to store data
         */
        static LerpToRef(left, right, amount, result) {
            result.r = left.r + (right.r - left.r) * amount;
            result.g = left.g + (right.g - left.g) * amount;
            result.b = left.b + (right.b - left.b) * amount;
            result.a = left.a + (right.a - left.a) * amount;
        }
        /**
         * Creates a new Color4 from the starting index element of the given array
         * @param array defines the source array to read from
         * @param offset defines the offset in the source array
         * @returns a new Color4 object
         */
        static FromArray(array, offset = 0) {
            return new Color4(array[offset], array[offset + 1], array[offset + 2], array[offset + 3]);
        }
        /**
         * Creates a new Color3 from integer values (< 256)
         * @param r defines the red component to read from (value between 0 and 255)
         * @param g defines the green component to read from (value between 0 and 255)
         * @param b defines the blue component to read from (value between 0 and 255)
         * @param a defines the alpha component to read from (value between 0 and 255)
         * @returns a new Color3 object
         */
        static FromInts(r, g, b, a) {
            return new Color4(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
        }
        /**
         * Check the content of a given array and convert it to an array containing RGBA data
         * If the original array was already containing count * 4 values then it is returned directly
         * @param colors defines the array to check
         * @param count defines the number of RGBA data to expect
         * @returns an array containing count * 4 values (RGBA)
         */
        static CheckColors4(colors, count) {
            // Check if color3 was used
            if (colors.length === count * 3) {
                let colors4 = [];
                for (let index = 0; index < colors.length; index += 3) {
                    let newIndex = (index / 3) * 4;
                    colors4[newIndex] = colors[index];
                    colors4[newIndex + 1] = colors[index + 1];
                    colors4[newIndex + 2] = colors[index + 2];
                    colors4[newIndex + 3] = 1.0;
                }
                return colors4;
            }
            return colors;
        }
    }
    FIREFLYX.Color4 = Color4;
    /**
     * Class representing a vector containing 2 coordinates
     */
    class Vector2 {
        /**
         * Creates a new Vector2 from the given x and y coordinates
         * @param x defines the first coordinate
         * @param y defines the second coordinate
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(
        /** defines the first coordinate */
        x = 0, 
        /** defines the second coordinate */
        y = 0) {
            this.x = x;
            this.y = y;
        }
        /**
         * Gets a string with the Vector2 coordinates
         * @returns a string with the Vector2 coordinates
         */
        toString() {
            return "{X: " + this.x + " Y:" + this.y + "}";
        }
        /**
         * Gets class name
         * @returns the string "Vector2"
         */
        getClassName() {
            return "Vector2";
        }
        /**
         * Gets current vector hash code
         * @returns the Vector2 hash code as a number
         */
        getHashCode() {
            let hash = this.x || 0;
            hash = (hash * 397) ^ (this.y || 0);
            return hash;
        }
        // Operators
        /**
         * Sets the Vector2 coordinates in the given array or Float32Array from the given index.
         * @param array defines the source array
         * @param index defines the offset in source array
         * @returns the current Vector2
         */
        toArray(array, index = 0) {
            array[index] = this.x;
            array[index + 1] = this.y;
            return this;
        }
        /**
         * Copy the current vector to an array
         * @returns a new array with 2 elements: the Vector2 coordinates.
         */
        asArray() {
            let result = new Array();
            this.toArray(result, 0);
            return result;
        }
        /**
         * Sets the Vector2 coordinates with the given Vector2 coordinates
         * @param source defines the source Vector2
         * @returns the current updated Vector2
         */
        copyFrom(source) {
            this.x = source.x;
            this.y = source.y;
            return this;
        }
        /**
         * Sets the Vector2 coordinates with the given floats
         * @param x defines the first coordinate
         * @param y defines the second coordinate
         * @returns the current updated Vector2
         */
        copyFromFloats(x, y) {
            this.x = x;
            this.y = y;
            return this;
        }
        /**
         * Sets the Vector2 coordinates with the given floats
         * @param x defines the first coordinate
         * @param y defines the second coordinate
         * @returns the current updated Vector2
         */
        set(x, y) {
            return this.copyFromFloats(x, y);
        }
        /**
         * Add another vector with the current one
         * @param otherVector defines the other vector
         * @returns a new Vector2 set with the addition of the current Vector2 and the given one coordinates
         */
        add(otherVector) {
            return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
        }
        /**
         * Sets the "result" coordinates with the addition of the current Vector2 and the given one coordinates
         * @param otherVector defines the other vector
         * @param result defines the target vector
         * @returns the unmodified current Vector2
         */
        addToRef(otherVector, result) {
            result.x = this.x + otherVector.x;
            result.y = this.y + otherVector.y;
            return this;
        }
        /**
         * Set the Vector2 coordinates by adding the given Vector2 coordinates
         * @param otherVector defines the other vector
         * @returns the current updated Vector2
         */
        addInPlace(otherVector) {
            this.x += otherVector.x;
            this.y += otherVector.y;
            return this;
        }
        /**
         * Gets a new Vector2 by adding the current Vector2 coordinates to the given Vector3 x, y coordinates
         * @param otherVector defines the other vector
         * @returns a new Vector2
         */
        addVector3(otherVector) {
            return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
        }
        /**
         * Gets a new Vector2 set with the subtracted coordinates of the given one from the current Vector2
         * @param otherVector defines the other vector
         * @returns a new Vector2
         */
        subtract(otherVector) {
            return new Vector2(this.x - otherVector.x, this.y - otherVector.y);
        }
        /**
         * Sets the "result" coordinates with the subtraction of the given one from the current Vector2 coordinates.
         * @param otherVector defines the other vector
         * @param result defines the target vector
         * @returns the unmodified current Vector2
         */
        subtractToRef(otherVector, result) {
            result.x = this.x - otherVector.x;
            result.y = this.y - otherVector.y;
            return this;
        }
        /**
         * Sets the current Vector2 coordinates by subtracting from it the given one coordinates
         * @param otherVector defines the other vector
         * @returns the current updated Vector2
         */
        subtractInPlace(otherVector) {
            this.x -= otherVector.x;
            this.y -= otherVector.y;
            return this;
        }
        /**
         * Multiplies in place the current Vector2 coordinates by the given ones
         * @param otherVector defines the other vector
         * @returns the current updated Vector2
         */
        multiplyInPlace(otherVector) {
            this.x *= otherVector.x;
            this.y *= otherVector.y;
            return this;
        }
        /**
         * Returns a new Vector2 set with the multiplication of the current Vector2 and the given one coordinates
         * @param otherVector defines the other vector
         * @returns a new Vector2
         */
        multiply(otherVector) {
            return new Vector2(this.x * otherVector.x, this.y * otherVector.y);
        }
        /**
         * Sets "result" coordinates with the multiplication of the current Vector2 and the given one coordinates
         * @param otherVector defines the other vector
         * @param result defines the target vector
         * @returns the unmodified current Vector2
         */
        multiplyToRef(otherVector, result) {
            result.x = this.x * otherVector.x;
            result.y = this.y * otherVector.y;
            return this;
        }
        /**
         * Gets a new Vector2 set with the Vector2 coordinates multiplied by the given floats
         * @param x defines the first coordinate
         * @param y defines the second coordinate
         * @returns a new Vector2
         */
        multiplyByFloats(x, y) {
            return new Vector2(this.x * x, this.y * y);
        }
        /**
         * Returns a new Vector2 set with the Vector2 coordinates divided by the given one coordinates
         * @param otherVector defines the other vector
         * @returns a new Vector2
         */
        divide(otherVector) {
            return new Vector2(this.x / otherVector.x, this.y / otherVector.y);
        }
        /**
         * Sets the "result" coordinates with the Vector2 divided by the given one coordinates
         * @param otherVector defines the other vector
         * @param result defines the target vector
         * @returns the unmodified current Vector2
         */
        divideToRef(otherVector, result) {
            result.x = this.x / otherVector.x;
            result.y = this.y / otherVector.y;
            return this;
        }
        /**
         * Divides the current Vector2 coordinates by the given ones
         * @param otherVector defines the other vector
         * @returns the current updated Vector2
         */
        divideInPlace(otherVector) {
            return this.divideToRef(otherVector, this);
        }
        /**
         * Gets a new Vector2 with current Vector2 negated coordinates
         * @returns a new Vector2
         */
        negate() {
            return new Vector2(-this.x, -this.y);
        }
        /**
         * Multiply the Vector2 coordinates by scale
         * @param scale defines the scaling factor
         * @returns the current updated Vector2
         */
        scaleInPlace(scale) {
            this.x *= scale;
            this.y *= scale;
            return this;
        }
        /**
         * Returns a new Vector2 scaled by "scale" from the current Vector2
         * @param scale defines the scaling factor
         * @returns a new Vector2
         */
        scale(scale) {
            let result = new Vector2(0, 0);
            this.scaleToRef(scale, result);
            return result;
        }
        /**
         * Scale the current Vector2 values by a factor to a given Vector2
         * @param scale defines the scale factor
         * @param result defines the Vector2 object where to store the result
         * @returns the unmodified current Vector2
         */
        scaleToRef(scale, result) {
            result.x = this.x * scale;
            result.y = this.y * scale;
            return this;
        }
        /**
         * Scale the current Vector2 values by a factor and add the result to a given Vector2
         * @param scale defines the scale factor
         * @param result defines the Vector2 object where to store the result
         * @returns the unmodified current Vector2
         */
        scaleAndAddToRef(scale, result) {
            result.x += this.x * scale;
            result.y += this.y * scale;
            return this;
        }
        /**
         * Gets a boolean if two vectors are equals
         * @param otherVector defines the other vector
         * @returns true if the given vector coordinates strictly equal the current Vector2 ones
         */
        equals(otherVector) {
            return otherVector && this.x === otherVector.x && this.y === otherVector.y;
        }
        /**
         * Gets a boolean if two vectors are equals (using an epsilon value)
         * @param otherVector defines the other vector
         * @param _epsilon defines the minimal distance to consider equality
         * @returns true if the given vector coordinates are close to the current ones by a distance of epsilon.
         */
        equalsWithEpsilon(otherVector, _epsilon = FIREFLYX.epsilon) {
            return otherVector && FIREFLYX.Scalar.WithinEpsilon(this.x, otherVector.x, _epsilon) && FIREFLYX.Scalar.WithinEpsilon(this.y, otherVector.y, _epsilon);
        }
        /**
         * Gets a new Vector2 from current Vector2 floored values
         * @returns a new Vector2
         */
        floor() {
            return new Vector2(Math.floor(this.x), Math.floor(this.y));
        }
        /**
         * Gets a new Vector2 from current Vector2 floored values
         * @returns a new Vector2
         */
        fract() {
            return new Vector2(this.x - Math.floor(this.x), this.y - Math.floor(this.y));
        }
        // Properties
        /**
         * Gets the length of the vector
         * @returns the vector length (float)
         */
        length() {
            return Math.sqrt(this.x * this.x + this.y * this.y);
        }
        /**
         * Gets the vector squared length
         * @returns the vector squared length (float)
         */
        lengthSquared() {
            return (this.x * this.x + this.y * this.y);
        }
        // Methods
        /**
         * Normalize the vector
         * @returns the current updated Vector2
         */
        normalize() {
            let len = this.length();
            if (len === 0)
                return this;
            let num = 1.0 / len;
            this.x *= num;
            this.y *= num;
            return this;
        }
        /**
         * Gets a new Vector2 copied from the Vector2
         * @returns a new Vector2
         */
        clone() {
            return new Vector2(this.x, this.y);
        }
        // Statics
        /**
         * Gets a new Vector2(0, 0)
         * @returns a new Vector2
         */
        static Zero() {
            return new Vector2(0, 0);
        }
        /**
         * Gets a new Vector2(1, 1)
         * @returns a new Vector2
         */
        static One() {
            return new Vector2(1, 1);
        }
        /**
         * Gets a new Vector2 set from the given index element of the given array
         * @param array defines the data source
         * @param offset defines the offset in the data source
         * @returns a new Vector2
         */
        static FromArray(array, offset = 0) {
            return new Vector2(array[offset], array[offset + 1]);
        }
        /**
         * Sets "result" from the given index element of the given array
         * @param array defines the data source
         * @param offset defines the offset in the data source
         * @param result defines the target vector
         */
        static FromArrayToRef(array, offset, result) {
            result.x = array[offset];
            result.y = array[offset + 1];
        }
        /**
         * Gets a new Vector2 located for "amount" (float) on the CatmullRom spline defined by the given four Vector2
         * @param value1 defines 1st point of control
         * @param value2 defines 2nd point of control
         * @param value3 defines 3rd point of control
         * @param value4 defines 4th point of control
         * @param amount defines the interpolation factor
         * @returns a new Vector2
         */
        static CatmullRom(value1, value2, value3, value4, amount) {
            let squared = amount * amount;
            let cubed = amount * squared;
            let x = 0.5 * ((((2.0 * value2.x) + ((-value1.x + value3.x) * amount)) +
                (((((2.0 * value1.x) - (5.0 * value2.x)) + (4.0 * value3.x)) - value4.x) * squared)) +
                ((((-value1.x + (3.0 * value2.x)) - (3.0 * value3.x)) + value4.x) * cubed));
            let y = 0.5 * ((((2.0 * value2.y) + ((-value1.y + value3.y) * amount)) +
                (((((2.0 * value1.y) - (5.0 * value2.y)) + (4.0 * value3.y)) - value4.y) * squared)) +
                ((((-value1.y + (3.0 * value2.y)) - (3.0 * value3.y)) + value4.y) * cubed));
            return new Vector2(x, y);
        }
        /**
         * Returns a new Vector2 set with same the coordinates than "value" ones if the vector "value" is in the square defined by "min" and "max".
         * If a coordinate of "value" is lower than "min" coordinates, the returned Vector2 is given this "min" coordinate.
         * If a coordinate of "value" is greater than "max" coordinates, the returned Vector2 is given this "max" coordinate
         * @param value defines the value to clamp
         * @param min defines the lower limit
         * @param max defines the upper limit
         * @returns a new Vector2
         */
        static Clamp(value, min, max) {
            let x = value.x;
            x = (x > max.x) ? max.x : x;
            x = (x < min.x) ? min.x : x;
            let y = value.y;
            y = (y > max.y) ? max.y : y;
            y = (y < min.y) ? min.y : y;
            return new Vector2(x, y);
        }
        /**
         * Returns a new Vector2 located for "amount" (float) on the Hermite spline defined by the vectors "value1", "value3", "tangent1", "tangent2"
         * @param value1 defines the 1st control point
         * @param tangent1 defines the outgoing tangent
         * @param value2 defines the 2nd control point
         * @param tangent2 defines the incoming tangent
         * @param amount defines the interpolation factor
         * @returns a new Vector2
         */
        static Hermite(value1, tangent1, value2, tangent2, amount) {
            let squared = amount * amount;
            let cubed = amount * squared;
            let part1 = ((2.0 * cubed) - (3.0 * squared)) + 1.0;
            let part2 = (-2.0 * cubed) + (3.0 * squared);
            let part3 = (cubed - (2.0 * squared)) + amount;
            let part4 = cubed - squared;
            let x = (((value1.x * part1) + (value2.x * part2)) + (tangent1.x * part3)) + (tangent2.x * part4);
            let y = (((value1.y * part1) + (value2.y * part2)) + (tangent1.y * part3)) + (tangent2.y * part4);
            return new Vector2(x, y);
        }
        /**
         * Returns a new Vector2 located for "amount" (float) on the linear interpolation between the vector "start" adn the vector "end".
         * @param start defines the start vector
         * @param end defines the end vector
         * @param amount defines the interpolation factor
         * @returns a new Vector2
         */
        static Lerp(start, end, amount) {
            let x = start.x + ((end.x - start.x) * amount);
            let y = start.y + ((end.y - start.y) * amount);
            return new Vector2(x, y);
        }
        /**
         * Gets the dot product of the vector "left" and the vector "right"
         * @param left defines first vector
         * @param right defines second vector
         * @returns the dot product (float)
         */
        static Dot(left, right) {
            return left.x * right.x + left.y * right.y;
        }
        /**
         * Returns a new Vector2 equal to the normalized given vector
         * @param vector defines the vector to normalize
         * @returns a new Vector2
         */
        static Normalize(vector) {
            let newVector = vector.clone();
            newVector.normalize();
            return newVector;
        }
        /**
         * Gets a new Vector2 set with the minimal coordinate values from the "left" and "right" vectors
         * @param left defines 1st vector
         * @param right defines 2nd vector
         * @returns a new Vector2
         */
        static Minimize(left, right) {
            let x = (left.x < right.x) ? left.x : right.x;
            let y = (left.y < right.y) ? left.y : right.y;
            return new Vector2(x, y);
        }
        /**
         * Gets a new Vecto2 set with the maximal coordinate values from the "left" and "right" vectors
         * @param left defines 1st vector
         * @param right defines 2nd vector
         * @returns a new Vector2
         */
        static Maximize(left, right) {
            let x = (left.x > right.x) ? left.x : right.x;
            let y = (left.y > right.y) ? left.y : right.y;
            return new Vector2(x, y);
        }
        /**
         * Gets a new Vector2 set with the transformed coordinates of the given vector by the given transformation matrix
         * @param vector defines the vector to transform
         * @param transformation defines the matrix to apply
         * @returns a new Vector2
         */
        static Transform(vector, transformation) {
            let r = Vector2.Zero();
            Vector2.TransformToRef(vector, transformation, r);
            return r;
        }
        /**
         * Transforms the given vector coordinates by the given transformation matrix and stores the result in the vector "result" coordinates
         * @param vector defines the vector to transform
         * @param transformation defines the matrix to apply
         * @param result defines the target vector
         */
        static TransformToRef(vector, transformation, result) {
            let x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + transformation.m[12];
            let y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + transformation.m[13];
            result.x = x;
            result.y = y;
        }
        /**
         * Determines if a given vector is included in a triangle
         * @param p defines the vector to test
         * @param p0 defines 1st triangle point
         * @param p1 defines 2nd triangle point
         * @param p2 defines 3rd triangle point
         * @returns true if the point "p" is in the triangle defined by the vertors "p0", "p1", "p2"
         */
        static PointInTriangle(p, p0, p1, p2) {
            let a = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
            let sign = a < 0 ? -1 : 1;
            let s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign;
            let t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign;
            return s > 0 && t > 0 && (s + t) < 2 * a * sign;
        }
        /**
         * Gets the distance between the vectors "value1" and "value2"
         * @param value1 defines first vector
         * @param value2 defines second vector
         * @returns the distance between vectors
         */
        static Distance(value1, value2) {
            return Math.sqrt(Vector2.DistanceSquared(value1, value2));
        }
        /**
         * Returns the squared distance between the vectors "value1" and "value2"
         * @param value1 defines first vector
         * @param value2 defines second vector
         * @returns the squared distance between vectors
         */
        static DistanceSquared(value1, value2) {
            let x = value1.x - value2.x;
            let y = value1.y - value2.y;
            return (x * x) + (y * y);
        }
        /**
         * Gets a new Vector2 located at the center of the vectors "value1" and "value2"
         * @param value1 defines first vector
         * @param value2 defines second vector
         * @returns a new Vector2
         */
        static Center(value1, value2) {
            let center = value1.add(value2);
            center.scaleInPlace(0.5);
            return center;
        }
        /**
         * Gets the shortest distance (float) between the point "p" and the segment defined by the two points "segA" and "segB".
         * @param p defines the middle point
         * @param segA defines one point of the segment
         * @param segB defines the other point of the segment
         * @returns the shortest distance
         */
        static DistanceOfPointFromSegment(p, segA, segB) {
            let l2 = Vector2.DistanceSquared(segA, segB);
            if (l2 === 0.0) {
                return Vector2.Distance(p, segA);
            }
            let v = segB.subtract(segA);
            let t = Math.max(0, Math.min(1, Vector2.Dot(p.subtract(segA), v) / l2));
            let proj = segA.add(v.multiplyByFloats(t, t));
            return Vector2.Distance(p, proj);
        }
    }
    FIREFLYX.Vector2 = Vector2;
    /**
     * Classed used to store (x,y,z) vector representation
     * A Vector3 is the main object used in 3D geometry
     * It can represent etiher the coordinates of a point the space, either a direction
     * Reminder: Babylon.js uses a left handed forward facing system
     */
    class Vector3 {
        /**
         * Creates a new Vector3 object from the given x, y, z (floats) coordinates.
         * @param x defines the first coordinates (on X axis)
         * @param y defines the second coordinates (on Y axis)
         * @param z defines the third coordinates (on Z axis)
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(
        /**
         * Defines the first coordinates (on X axis)
         */
        x = 0, 
        /**
         * Defines the second coordinates (on Y axis)
         */
        y = 0, 
        /**
         * Defines the third coordinates (on Z axis)
         */
        z = 0) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        /**
         * Creates a string representation of the Vector3
         * @returns a string with the Vector3 coordinates.
         */
        toString() {
            return "{X: " + this.x + " Y:" + this.y + " Z:" + this.z + "}";
        }
        /**
         * Gets the class name
         * @returns the string "Vector3"
         */
        getClassName() {
            return "Vector3";
        }
        /**
         * Creates the Vector3 hash code
         * @returns a number which tends to be unique between Vector3 instances
         */
        getHashCode() {
            let hash = this.x || 0;
            hash = (hash * 397) ^ (this.y || 0);
            hash = (hash * 397) ^ (this.z || 0);
            return hash;
        }
        // Operators
        /**
         * Creates an array containing three elements : the coordinates of the Vector3
         * @returns a new array of numbers
         */
        asArray() {
            let result = [];
            this.toArray(result, 0);
            return result;
        }
        /**
         * Populates the given array or Float32Array from the given index with the successive coordinates of the Vector3
         * @param array defines the destination array
         * @param index defines the offset in the destination array
         * @returns the current Vector3
         */
        toArray(array, index = 0) {
            array[index] = this.x;
            array[index + 1] = this.y;
            array[index + 2] = this.z;
            return this;
        }
        /**
         * Converts the current Vector3 into a quaternion (considering that the Vector3 contains Euler angles representation of a rotation)
         * @returns a new Quaternion object, computed from the Vector3 coordinates
         */
        toQuaternion() {
            return FIREFLYX.Quaternion.RotationYawPitchRoll(this.x, this.y, this.z);
        }
        /**
         * Adds the given vector to the current Vector3
         * @param otherVector defines the second operand
         * @returns the current updated Vector3
         */
        addInPlace(otherVector) {
            this.x += otherVector.x;
            this.y += otherVector.y;
            this.z += otherVector.z;
            return this;
        }
        /**
         * Gets a new Vector3, result of the addition the current Vector3 and the given vector
         * @param otherVector defines the second operand
         * @returns the resulting Vector3
         */
        add(otherVector) {
            return new Vector3(this.x + otherVector.x, this.y + otherVector.y, this.z + otherVector.z);
        }
        /**
         * Adds the current Vector3 to the given one and stores the result in the vector "result"
         * @param otherVector defines the second operand
         * @param result defines the Vector3 object where to store the result
         * @returns the current Vector3
         */
        addToRef(otherVector, result) {
            result.x = this.x + otherVector.x;
            result.y = this.y + otherVector.y;
            result.z = this.z + otherVector.z;
            return this;
        }
        /**
         * Subtract the given vector from the current Vector3
         * @param otherVector defines the second operand
         * @returns the current updated Vector3
         */
        subtractInPlace(otherVector) {
            this.x -= otherVector.x;
            this.y -= otherVector.y;
            this.z -= otherVector.z;
            return this;
        }
        /**
         * Returns a new Vector3, result of the subtraction of the given vector from the current Vector3
         * @param otherVector defines the second operand
         * @returns the resulting Vector3
         */
        subtract(otherVector) {
            return new Vector3(this.x - otherVector.x, this.y - otherVector.y, this.z - otherVector.z);
        }
        /**
         * Subtracts the given vector from the current Vector3 and stores the result in the vector "result".
         * @param otherVector defines the second operand
         * @param result defines the Vector3 object where to store the result
         * @returns the current Vector3
         */
        subtractToRef(otherVector, result) {
            result.x = this.x - otherVector.x;
            result.y = this.y - otherVector.y;
            result.z = this.z - otherVector.z;
            return this;
        }
        /**
         * Returns a new Vector3 set with the subtraction of the given floats from the current Vector3 coordinates
         * @param x defines the x coordinate of the operand
         * @param y defines the y coordinate of the operand
         * @param z defines the z coordinate of the operand
         * @returns the resulting Vector3
         */
        subtractFromFloats(x, y, z) {
            return new Vector3(this.x - x, this.y - y, this.z - z);
        }
        /**
         * Subtracts the given floats from the current Vector3 coordinates and set the given vector "result" with this result
         * @param x defines the x coordinate of the operand
         * @param y defines the y coordinate of the operand
         * @param z defines the z coordinate of the operand
         * @param result defines the Vector3 object where to store the result
         * @returns the current Vector3
         */
        subtractFromFloatsToRef(x, y, z, result) {
            result.x = this.x - x;
            result.y = this.y - y;
            result.z = this.z - z;
            return this;
        }
        /**
         * Gets a new Vector3 set with the current Vector3 negated coordinates
         * @returns a new Vector3
         */
        negate() {
            return new Vector3(-this.x, -this.y, -this.z);
        }
        /**
         * Multiplies the Vector3 coordinates by the float "scale"
         * @param scale defines the multiplier factor
         * @returns the current updated Vector3
         */
        scaleInPlace(scale) {
            this.x *= scale;
            this.y *= scale;
            this.z *= scale;
            return this;
        }
        /**
         * Returns a new Vector3 set with the current Vector3 coordinates multiplied by the float "scale"
         * @param scale defines the multiplier factor
         * @returns a new Vector3
         */
        scale(scale) {
            return new Vector3(this.x * scale, this.y * scale, this.z * scale);
        }
        /**
         * Multiplies the current Vector3 coordinates by the float "scale" and stores the result in the given vector "result" coordinates
         * @param scale defines the multiplier factor
         * @param result defines the Vector3 object where to store the result
         * @returns the current Vector3
         */
        scaleToRef(scale, result) {
            result.x = this.x * scale;
            result.y = this.y * scale;
            result.z = this.z * scale;
            return this;
        }
        /**
         * Scale the current Vector3 values by a factor and add the result to a given Vector3
         * @param scale defines the scale factor
         * @param result defines the Vector3 object where to store the result
         * @returns the unmodified current Vector3
         */
        scaleAndAddToRef(scale, result) {
            result.x += this.x * scale;
            result.y += this.y * scale;
            result.z += this.z * scale;
            return this;
        }
        /**
         * Returns true if the current Vector3 and the given vector coordinates are strictly equal
         * @param otherVector defines the second operand
         * @returns true if both vectors are equals
         */
        equals(otherVector) {
            return otherVector && this.x === otherVector.x && this.y === otherVector.y && this.z === otherVector.z;
        }
        /**
         * Returns true if the current Vector3 and the given vector coordinates are distant less than epsilon
         * @param otherVector defines the second operand
         * @param _epsilon defines the minimal distance to define values as equals
         * @returns true if both vectors are distant less than epsilon
         */
        equalsWithEpsilon(otherVector, _epsilon = FIREFLYX.epsilon) {
            return otherVector && FIREFLYX.Scalar.WithinEpsilon(this.x, otherVector.x, _epsilon) &&
                FIREFLYX.Scalar.WithinEpsilon(this.y, otherVector.y, _epsilon) && FIREFLYX.Scalar.WithinEpsilon(this.z, otherVector.z, _epsilon);
        }
        /**
         * Returns true if the current Vector3 coordinates equals the given floats
         * @param x defines the x coordinate of the operand
         * @param y defines the y coordinate of the operand
         * @param z defines the z coordinate of the operand
         * @returns true if both vectors are equals
         */
        equalsToFloats(x, y, z) {
            return this.x === x && this.y === y && this.z === z;
        }
        /**
         * Multiplies the current Vector3 coordinates by the given ones
         * @param otherVector defines the second operand
         * @returns the current updated Vector3
         */
        multiplyInPlace(otherVector) {
            this.x *= otherVector.x;
            this.y *= otherVector.y;
            this.z *= otherVector.z;
            return this;
        }
        /**
         * Returns a new Vector3, result of the multiplication of the current Vector3 by the given vector
         * @param otherVector defines the second operand
         * @returns the new Vector3
         */
        multiply(otherVector) {
            return new Vector3(this.x * otherVector.x, this.y * otherVector.y, this.z * otherVector.z);
        }
        /**
         * Multiplies the current Vector3 by the given one and stores the result in the given vector "result"
         * @param otherVector defines the second operand
         * @param result defines the Vector3 object where to store the result
         * @returns the current Vector3
         */
        multiplyToRef(otherVector, result) {
            result.x = this.x * otherVector.x;
            result.y = this.y * otherVector.y;
            result.z = this.z * otherVector.z;
            return this;
        }
        /**
         * Returns a new Vector3 set with the result of the mulliplication of the current Vector3 coordinates by the given floats
         * @param x defines the x coordinate of the operand
         * @param y defines the y coordinate of the operand
         * @param z defines the z coordinate of the operand
         * @returns the new Vector3
         */
        multiplyByFloats(x, y, z) {
            return new Vector3(this.x * x, this.y * y, this.z * z);
        }
        /**
         * Returns a new Vector3 set with the result of the division of the current Vector3 coordinates by the given ones
         * @param otherVector defines the second operand
         * @returns the new Vector3
         */
        divide(otherVector) {
            return new Vector3(this.x / otherVector.x, this.y / otherVector.y, this.z / otherVector.z);
        }
        /**
         * Divides the current Vector3 coordinates by the given ones and stores the result in the given vector "result"
         * @param otherVector defines the second operand
         * @param result defines the Vector3 object where to store the result
         * @returns the current Vector3
         */
        divideToRef(otherVector, result) {
            result.x = this.x / otherVector.x;
            result.y = this.y / otherVector.y;
            result.z = this.z / otherVector.z;
            return this;
        }
        /**
         * Divides the current Vector3 coordinates by the given ones.
         * @param otherVector defines the second operand
         * @returns the current updated Vector3
         */
        divideInPlace(otherVector) {
            return this.divideToRef(otherVector, this);
        }
        /**
         * Updates the current Vector3 with the minimal coordinate values between its and the given vector ones
         * @param other defines the second operand
         * @returns the current updated Vector3
         */
        minimizeInPlace(other) {
            if (other.x < this.x)
                this.x = other.x;
            if (other.y < this.y)
                this.y = other.y;
            if (other.z < this.z)
                this.z = other.z;
            return this;
        }
        /**
         * Updates the current Vector3 with the maximal coordinate values between its and the given vector ones.
         * @param other defines the second operand
         * @returns the current updated Vector3
         */
        maximizeInPlace(other) {
            if (other.x > this.x)
                this.x = other.x;
            if (other.y > this.y)
                this.y = other.y;
            if (other.z > this.z)
                this.z = other.z;
            return this;
        }
        /**
         * Gets a boolean indicating that the vector is non uniform meaning x, y or z are not all the same
         */
        get isNonUniform() {
            let absX = Math.abs(this.x);
            let absY = Math.abs(this.y);
            if (absX !== absY) {
                return true;
            }
            let absZ = Math.abs(this.z);
            if (absX !== absZ) {
                return true;
            }
            if (absY !== absZ) {
                return true;
            }
            return false;
        }
        /**
         * Gets a new Vector3 from current Vector3 floored values
         * @returns a new Vector3
         */
        floor() {
            return new Vector3(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
        }
        /**
         * Gets a new Vector3 from current Vector3 floored values
         * @returns a new Vector3
         */
        fract() {
            return new Vector3(this.x - Math.floor(this.x), this.y - Math.floor(this.y), this.z - Math.floor(this.z));
        }
        // Properties
        /**
         * Gets the length of the Vector3
         * @returns the length of the Vecto3
         */
        length() {
            return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
        }
        /**
         * Gets the squared length of the Vector3
         * @returns squared length of the Vector3
         */
        lengthSquared() {
            return (this.x * this.x + this.y * this.y + this.z * this.z);
        }
        /**
         * Normalize the current Vector3.
         * Please note that this is an in place operation.
         * @returns the current updated Vector3
         */
        normalize() {
            let len = this.length();
            if (len === 0 || len === 1.0)
                return this;
            let num = 1.0 / len;
            this.x *= num;
            this.y *= num;
            this.z *= num;
            return this;
        }
        /**
         * Normalize the current Vector3 to a new vector
         * @returns the new Vector3
         */
        normalizeToNew() {
            const normalized = new Vector3(0, 0, 0);
            this.normalizeToRef(normalized);
            return normalized;
        }
        /**
         * Normalize the current Vector3 to the reference
         * @param reference define the Vector3 to update
         * @returns the updated Vector3
         */
        normalizeToRef(reference) {
            let len = this.length();
            if (len === 0 || len === 1.0) {
                reference.set(this.x, this.y, this.z);
                return reference;
            }
            const scale = 1.0 / len;
            this.scaleToRef(scale, reference);
            return reference;
        }
        /**
         * Creates a new Vector3 copied from the current Vector3
         * @returns the new Vector3
         */
        clone() {
            return new Vector3(this.x, this.y, this.z);
        }
        /**
         * Copies the given vector coordinates to the current Vector3 ones
         * @param source defines the source Vector3
         * @returns the current updated Vector3
         */
        copyFrom(source) {
            this.x = source.x;
            this.y = source.y;
            this.z = source.z;
            return this;
        }
        /**
         * Copies the given floats to the current Vector3 coordinates
         * @param x defines the x coordinate of the operand
         * @param y defines the y coordinate of the operand
         * @param z defines the z coordinate of the operand
         * @returns the current updated Vector3
         */
        copyFromFloats(x, y, z) {
            this.x = x;
            this.y = y;
            this.z = z;
            return this;
        }
        /**
         * Copies the given floats to the current Vector3 coordinates
         * @param x defines the x coordinate of the operand
         * @param y defines the y coordinate of the operand
         * @param z defines the z coordinate of the operand
         * @returns the current updated Vector3
         */
        set(x, y, z) {
            return this.copyFromFloats(x, y, z);
        }
        // Statics
        /**
         * Get the clip factor between two vectors
         * @param vector0 defines the first operand
         * @param vector1 defines the second operand
         * @param axis defines the axis to use
         * @param size defines the size along the axis
         * @returns the clip factor
         */
        static GetClipFactor(vector0, vector1, axis, size) {
            let d0 = Vector3.Dot(vector0, axis) - size;
            let d1 = Vector3.Dot(vector1, axis) - size;
            let s = d0 / (d0 - d1);
            return s;
        }
        /**
         * Get angle between two vectors
         * @param vector0 angle between vector0 and vector1
         * @param vector1 angle between vector0 and vector1
         * @param normal direction of the normal
         * @return the angle between vector0 and vector1
         */
        static GetAngleBetweenVectors(vector0, vector1, normal) {
            let v0 = vector0.clone()
                .normalize();
            let v1 = vector1.clone()
                .normalize();
            let dot = Vector3.Dot(v0, v1);
            let n = Vector3.Cross(v0, v1);
            if (Vector3.Dot(n, normal) > 0) {
                return Math.acos(dot);
            }
            return -Math.acos(dot);
        }
        /**
         * Returns a new Vector3 set from the index "offset" of the given array
         * @param array defines the source array
         * @param offset defines the offset in the source array
         * @returns the new Vector3
         */
        static FromArray(array, offset) {
            if (!offset) {
                offset = 0;
            }
            return new Vector3(array[offset], array[offset + 1], array[offset + 2]);
        }
        /**
         * Returns a new Vector3 set from the index "offset" of the given Float32Array
         * This function is deprecated.  Use FromArray instead
         * @param array defines the source array
         * @param offset defines the offset in the source array
         * @returns the new Vector3
         */
        static FromFloatArray(array, offset) {
            return Vector3.FromArray(array, offset);
        }
        /**
         * Sets the given vector "result" with the element values from the index "offset" of the given array
         * @param array defines the source array
         * @param offset defines the offset in the source array
         * @param result defines the Vector3 where to store the result
         */
        static FromArrayToRef(array, offset, result) {
            result.x = array[offset];
            result.y = array[offset + 1];
            result.z = array[offset + 2];
        }
        /**
         * Sets the given vector "result" with the element values from the index "offset" of the given Float32Array
         * This function is deprecated.  Use FromArrayToRef instead.
         * @param array defines the source array
         * @param offset defines the offset in the source array
         * @param result defines the Vector3 where to store the result
         */
        static FromFloatArrayToRef(array, offset, result) {
            return Vector3.FromArrayToRef(array, offset, result);
        }
        /**
         * Sets the given vector "result" with the given floats.
         * @param x defines the x coordinate of the source
         * @param y defines the y coordinate of the source
         * @param z defines the z coordinate of the source
         * @param result defines the Vector3 where to store the result
         */
        static FromFloatsToRef(x, y, z, result) {
            result.x = x;
            result.y = y;
            result.z = z;
        }
        /**
         * Returns a new Vector3 set to (0.0, 0.0, 0.0)
         * @returns a new empty Vector3
         */
        static Zero() {
            return new Vector3(0.0, 0.0, 0.0);
        }
        /**
         * Returns a new Vector3 set to (1.0, 1.0, 1.0)
         * @returns a new unit Vector3
         */
        static One() {
            return new Vector3(1.0, 1.0, 1.0);
        }
        /**
         * Returns a new Vector3 set to (0.0, 1.0, 0.0)
         * @returns a new up Vector3
         */
        static Up() {
            return new Vector3(0.0, 1.0, 0.0);
        }
        /**
         * Returns a new Vector3 set to (0.0, -1.0, 0.0)
         * @returns a new down Vector3
         */
        static Down() {
            return new Vector3(0.0, -1.0, 0.0);
        }
        /**
         * Returns a new Vector3 set to (0.0, 0.0, 1.0)
         * @returns a new forward Vector3
         */
        static Forward() {
            return new Vector3(0.0, 0.0, 1.0);
        }
        /**
         * Returns a new Vector3 set to (1.0, 0.0, 0.0)
         * @returns a new right Vector3
         */
        static Right() {
            return new Vector3(1.0, 0.0, 0.0);
        }
        /**
         * Returns a new Vector3 set to (-1.0, 0.0, 0.0)
         * @returns a new left Vector3
         */
        static Left() {
            return new Vector3(-1.0, 0.0, 0.0);
        }
        /**
         * Returns a new Vector3 set with the result of the transformation by the given matrix of the given vector.
         * This method computes tranformed coordinates only, not transformed direction vectors (ie. it takes translation in account)
         * @param vector defines the Vector3 to transform
         * @param transformation defines the transformation matrix
         * @returns the transformed Vector3
         */
        static TransformCoordinates(vector, transformation) {
            let result = Vector3.Zero();
            Vector3.TransformCoordinatesToRef(vector, transformation, result);
            return result;
        }
        /**
         * Sets the given vector "result" coordinates with the result of the transformation by the given matrix of the given vector
         * This method computes tranformed coordinates only, not transformed direction vectors (ie. it takes translation in account)
         * @param vector defines the Vector3 to transform
         * @param transformation defines the transformation matrix
         * @param result defines the Vector3 where to store the result
         */
        static TransformCoordinatesToRef(vector, transformation, result) {
            let x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]) + transformation.m[12];
            let y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]) + transformation.m[13];
            let z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]) + transformation.m[14];
            let w = (vector.x * transformation.m[3]) + (vector.y * transformation.m[7]) + (vector.z * transformation.m[11]) + transformation.m[15];
            result.x = x / w;
            result.y = y / w;
            result.z = z / w;
        }
        /**
         * Sets the given vector "result" coordinates with the result of the transformation by the given matrix of the given floats (x, y, z)
         * This method computes tranformed coordinates only, not transformed direction vectors
         * @param x define the x coordinate of the source vector
         * @param y define the y coordinate of the source vector
         * @param z define the z coordinate of the source vector
         * @param transformation defines the transformation matrix
         * @param result defines the Vector3 where to store the result
         */
        static TransformCoordinatesFromFloatsToRef(x, y, z, transformation, result) {
            let rx = (x * transformation.m[0]) + (y * transformation.m[4]) + (z * transformation.m[8]) + transformation.m[12];
            let ry = (x * transformation.m[1]) + (y * transformation.m[5]) + (z * transformation.m[9]) + transformation.m[13];
            let rz = (x * transformation.m[2]) + (y * transformation.m[6]) + (z * transformation.m[10]) + transformation.m[14];
            let rw = (x * transformation.m[3]) + (y * transformation.m[7]) + (z * transformation.m[11]) + transformation.m[15];
            result.x = rx / rw;
            result.y = ry / rw;
            result.z = rz / rw;
        }
        /**
         * Returns a new Vector3 set with the result of the normal transformation by the given matrix of the given vector
         * This methods computes transformed normalized direction vectors only (ie. it does not apply translation)
         * @param vector defines the Vector3 to transform
         * @param transformation defines the transformation matrix
         * @returns the new Vector3
         */
        static TransformNormal(vector, transformation) {
            let result = Vector3.Zero();
            Vector3.TransformNormalToRef(vector, transformation, result);
            return result;
        }
        /**
         * Sets the given vector "result" with the result of the normal transformation by the given matrix of the given vector
         * This methods computes transformed normalized direction vectors only (ie. it does not apply translation)
         * @param vector defines the Vector3 to transform
         * @param transformation defines the transformation matrix
         * @param result defines the Vector3 where to store the result
         */
        static TransformNormalToRef(vector, transformation, result) {
            let x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
            let y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
            let z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
            result.x = x;
            result.y = y;
            result.z = z;
        }
        /**
         * Sets the given vector "result" with the result of the normal transformation by the given matrix of the given floats (x, y, z)
         * This methods computes transformed normalized direction vectors only (ie. it does not apply translation)
         * @param x define the x coordinate of the source vector
         * @param y define the y coordinate of the source vector
         * @param z define the z coordinate of the source vector
         * @param transformation defines the transformation matrix
         * @param result defines the Vector3 where to store the result
         */
        static TransformNormalFromFloatsToRef(x, y, z, transformation, result) {
            result.x = (x * transformation.m[0]) + (y * transformation.m[4]) + (z * transformation.m[8]);
            result.y = (x * transformation.m[1]) + (y * transformation.m[5]) + (z * transformation.m[9]);
            result.z = (x * transformation.m[2]) + (y * transformation.m[6]) + (z * transformation.m[10]);
        }
        /**
         * Returns a new Vector3 located for "amount" on the CatmullRom interpolation spline defined by
         * the vectors "value1", "value2", "value3", "value4"
         * @param value1 defines the first control point
         * @param value2 defines the second control point
         * @param value3 defines the third control point
         * @param value4 defines the fourth control point
         * @param amount defines the amount on the spline to use
         * @returns the new Vector3
         */
        static CatmullRom(value1, value2, value3, value4, amount) {
            let squared = amount * amount;
            let cubed = amount * squared;
            let x = 0.5 * ((((2.0 * value2.x) + ((-value1.x + value3.x) * amount)) +
                (((((2.0 * value1.x) - (5.0 * value2.x)) + (4.0 * value3.x)) - value4.x) * squared)) +
                ((((-value1.x + (3.0 * value2.x)) - (3.0 * value3.x)) + value4.x) * cubed));
            let y = 0.5 * ((((2.0 * value2.y) + ((-value1.y + value3.y) * amount)) +
                (((((2.0 * value1.y) - (5.0 * value2.y)) + (4.0 * value3.y)) - value4.y) * squared)) +
                ((((-value1.y + (3.0 * value2.y)) - (3.0 * value3.y)) + value4.y) * cubed));
            let z = 0.5 * ((((2.0 * value2.z) + ((-value1.z + value3.z) * amount)) +
                (((((2.0 * value1.z) - (5.0 * value2.z)) + (4.0 * value3.z)) - value4.z) * squared)) +
                ((((-value1.z + (3.0 * value2.z)) - (3.0 * value3.z)) + value4.z) * cubed));
            return new Vector3(x, y, z);
        }
        /**
         * Returns a new Vector3 set with the coordinates of "value", if the vector "value" is in the cube defined by the vectors "min" and "max"
         * If a coordinate value of "value" is lower than one of the "min" coordinate, then this "value" coordinate is set with the "min" one
         * If a coordinate value of "value" is greater than one of the "max" coordinate, then this "value" coordinate is set with the "max" one
         * @param value defines the current value
         * @param min defines the lower range value
         * @param max defines the upper range value
         * @returns the new Vector3
         */
        static Clamp(value, min, max) {
            let x = value.x;
            x = (x > max.x) ? max.x : x;
            x = (x < min.x) ? min.x : x;
            let y = value.y;
            y = (y > max.y) ? max.y : y;
            y = (y < min.y) ? min.y : y;
            let z = value.z;
            z = (z > max.z) ? max.z : z;
            z = (z < min.z) ? min.z : z;
            return new Vector3(x, y, z);
        }
        /**
         * Returns a new Vector3 located for "amount" (float) on the Hermite interpolation spline defined by the vectors "value1",
         * "tangent1", "value2", "tangent2"
         * @param value1 defines the first control point
         * @param tangent1 defines the first tangent vector
         * @param value2 defines the second control point
         * @param tangent2 defines the second tangent vector
         * @param amount defines the amount on the interpolation spline (between 0 and 1)
         * @returns the new Vector3
         */
        static Hermite(value1, tangent1, value2, tangent2, amount) {
            let squared = amount * amount;
            let cubed = amount * squared;
            let part1 = ((2.0 * cubed) - (3.0 * squared)) + 1.0;
            let part2 = (-2.0 * cubed) + (3.0 * squared);
            let part3 = (cubed - (2.0 * squared)) + amount;
            let part4 = cubed - squared;
            let x = (((value1.x * part1) + (value2.x * part2)) + (tangent1.x * part3)) + (tangent2.x * part4);
            let y = (((value1.y * part1) + (value2.y * part2)) + (tangent1.y * part3)) + (tangent2.y * part4);
            let z = (((value1.z * part1) + (value2.z * part2)) + (tangent1.z * part3)) + (tangent2.z * part4);
            return new Vector3(x, y, z);
        }
        /**
         * Returns a new Vector3 located for "amount" (float) on the linear interpolation between the vectors "start" and "end"
         * @param start defines the start value
         * @param end defines the end value
         * @param amount max defines amount between both (between 0 and 1)
         * @returns the new Vector3
         */
        static Lerp(start, end, amount) {
            let result = new Vector3(0, 0, 0);
            Vector3.LerpToRef(start, end, amount, result);
            return result;
        }
        /**
         * Sets the given vector "result" with the result of the linear interpolation from the vector "start" for "amount" to the vector "end"
         * @param start defines the start value
         * @param end defines the end value
         * @param amount max defines amount between both (between 0 and 1)
         * @param result defines the Vector3 where to store the result
         */
        static LerpToRef(start, end, amount, result) {
            result.x = start.x + ((end.x - start.x) * amount);
            result.y = start.y + ((end.y - start.y) * amount);
            result.z = start.z + ((end.z - start.z) * amount);
        }
        /**
         * Returns the dot product (float) between the vectors "left" and "right"
         * @param left defines the left operand
         * @param right defines the right operand
         * @returns the dot product
         */
        static Dot(left, right) {
            return (left.x * right.x + left.y * right.y + left.z * right.z);
        }
        /**
         * Returns a new Vector3 as the cross product of the vectors "left" and "right"
         * The cross product is then orthogonal to both "left" and "right"
         * @param left defines the left operand
         * @param right defines the right operand
         * @returns the cross product
         */
        static Cross(left, right) {
            let result = Vector3.Zero();
            Vector3.CrossToRef(left, right, result);
            return result;
        }
        /**
         * Sets the given vector "result" with the cross product of "left" and "right"
         * The cross product is then orthogonal to both "left" and "right"
         * @param left defines the left operand
         * @param right defines the right operand
         * @param result defines the Vector3 where to store the result
         */
        static CrossToRef(left, right, result) {
            MathTmp.Vector3[0].x = left.y * right.z - left.z * right.y;
            MathTmp.Vector3[0].y = left.z * right.x - left.x * right.z;
            MathTmp.Vector3[0].z = left.x * right.y - left.y * right.x;
            result.copyFrom(MathTmp.Vector3[0]);
        }
        /**
         * Returns a new Vector3 as the normalization of the given vector
         * @param vector defines the Vector3 to normalize
         * @returns the new Vector3
         */
        static Normalize(vector) {
            let result = Vector3.Zero();
            Vector3.NormalizeToRef(vector, result);
            return result;
        }
        /**
         * Sets the given vector "result" with the normalization of the given first vector
         * @param vector defines the Vector3 to normalize
         * @param result defines the Vector3 where to store the result
         */
        static NormalizeToRef(vector, result) {
            result.copyFrom(vector);
            result.normalize();
        }
        /**
         * Project a Vector3 onto screen space
         * @param vector defines the Vector3 to project
         * @param world defines the world matrix to use
         * @param transform defines the transform (view x projection) matrix to use
         * @param viewport defines the screen viewport to use
         * @returns the new Vector3
         */
        static Project(vector, world, transform, viewport) {
            let cw = viewport.w;
            let ch = viewport.h;
            let cx = viewport.x;
            let cy = viewport.y;
            let viewportMatrix = Vector3._viewportMatrixCache ? Vector3._viewportMatrixCache : (Vector3._viewportMatrixCache = new Matrix());
            Matrix.FromValuesToRef(cw / 2.0, 0, 0, 0, 0, -ch / 2.0, 0, 0, 0, 0, 0.5, 0, cx + cw / 2.0, ch / 2.0 + cy, 0.5, 1, viewportMatrix);
            let matrix = MathTmp.Matrix[0];
            world.multiplyToRef(transform, matrix);
            matrix.multiplyToRef(viewportMatrix, matrix);
            return Vector3.TransformCoordinates(vector, matrix);
        }
        /**
         * Unproject from screen space to object space
         * @param source defines the screen space Vector3 to use
         * @param viewportWidth defines the current width of the viewport
         * @param viewportHeight defines the current height of the viewport
         * @param world defines the world matrix to use (can be set to Identity to go to world space)
         * @param transform defines the transform (view x projection) matrix to use
         * @returns the new Vector3
         */
        static UnprojectFromTransform(source, viewportWidth, viewportHeight, world, transform) {
            let matrix = MathTmp.Matrix[0];
            world.multiplyToRef(transform, matrix);
            matrix.invert();
            source.x = source.x / viewportWidth * 2 - 1;
            source.y = -(source.y / viewportHeight * 2 - 1);
            let vector = Vector3.TransformCoordinates(source, matrix);
            let num = source.x * matrix.m[3] + source.y * matrix.m[7] + source.z * matrix.m[11] + matrix.m[15];
            if (FIREFLYX.Scalar.WithinEpsilon(num, 1.0)) {
                vector = vector.scale(1.0 / num);
            }
            return vector;
        }
        /**
         * Unproject from screen space to object space
         * @param source defines the screen space Vector3 to use
         * @param viewportWidth defines the current width of the viewport
         * @param viewportHeight defines the current height of the viewport
         * @param world defines the world matrix to use (can be set to Identity to go to world space)
         * @param view defines the view matrix to use
         * @param projection defines the projection matrix to use
         * @returns the new Vector3
         */
        static Unproject(source, viewportWidth, viewportHeight, world, view, projection) {
            let result = Vector3.Zero();
            Vector3.UnprojectToRef(source, viewportWidth, viewportHeight, world, view, projection, result);
            return result;
        }
        /**
         * Unproject from screen space to object space
         * @param source defines the screen space Vector3 to use
         * @param viewportWidth defines the current width of the viewport
         * @param viewportHeight defines the current height of the viewport
         * @param world defines the world matrix to use (can be set to Identity to go to world space)
         * @param view defines the view matrix to use
         * @param projection defines the projection matrix to use
         * @param result defines the Vector3 where to store the result
         */
        static UnprojectToRef(source, viewportWidth, viewportHeight, world, view, projection, result) {
            Vector3.UnprojectFloatsToRef(source.x, source.y, source.z, viewportWidth, viewportHeight, world, view, projection, result);
        }
        /**
         * Unproject from screen space to object space
         * @param sourceX defines the screen space x coordinate to use
         * @param sourceY defines the screen space y coordinate to use
         * @param sourceZ defines the screen space z coordinate to use
         * @param viewportWidth defines the current width of the viewport
         * @param viewportHeight defines the current height of the viewport
         * @param world defines the world matrix to use (can be set to Identity to go to world space)
         * @param view defines the view matrix to use
         * @param projection defines the projection matrix to use
         * @param result defines the Vector3 where to store the result
         */
        static UnprojectFloatsToRef(sourceX, sourceY, sourceZ, viewportWidth, viewportHeight, world, view, projection, result) {
            let matrix = MathTmp.Matrix[0];
            world.multiplyToRef(view, matrix);
            matrix.multiplyToRef(projection, matrix);
            matrix.invert();
            let screenSource = MathTmp.Vector3[0];
            screenSource.x = sourceX / viewportWidth * 2 - 1;
            screenSource.y = -(sourceY / viewportHeight * 2 - 1);
            screenSource.z = 2 * sourceZ - 1.0;
            Vector3.TransformCoordinatesToRef(screenSource, matrix, result);
            let num = screenSource.x * matrix.m[3] + screenSource.y * matrix.m[7] + screenSource.z * matrix.m[11] + matrix.m[15];
            if (FIREFLYX.Scalar.WithinEpsilon(num, 1.0)) {
                result.scaleInPlace(1.0 / num);
            }
        }
        /**
         * Gets the minimal coordinate values between two Vector3
         * @param left defines the first operand
         * @param right defines the second operand
         * @returns the new Vector3
         */
        static Minimize(left, right) {
            let min = left.clone();
            min.minimizeInPlace(right);
            return min;
        }
        /**
         * Gets the maximal coordinate values between two Vector3
         * @param left defines the first operand
         * @param right defines the second operand
         * @returns the new Vector3
         */
        static Maximize(left, right) {
            let max = left.clone();
            max.maximizeInPlace(right);
            return max;
        }
        /**
         * Returns the distance between the vectors "value1" and "value2"
         * @param value1 defines the first operand
         * @param value2 defines the second operand
         * @returns the distance
         */
        static Distance(value1, value2) {
            return Math.sqrt(Vector3.DistanceSquared(value1, value2));
        }
        /**
         * Returns the squared distance between the vectors "value1" and "value2"
         * @param value1 defines the first operand
         * @param value2 defines the second operand
         * @returns the squared distance
         */
        static DistanceSquared(value1, value2) {
            let x = value1.x - value2.x;
            let y = value1.y - value2.y;
            let z = value1.z - value2.z;
            return (x * x) + (y * y) + (z * z);
        }
        /**
         * Returns a new Vector3 located at the center between "value1" and "value2"
         * @param value1 defines the first operand
         * @param value2 defines the second operand
         * @returns the new Vector3
         */
        static Center(value1, value2) {
            let center = value1.add(value2);
            center.scaleInPlace(0.5);
            return center;
        }
        /**
         * Given three orthogonal normalized left-handed oriented Vector3 axis in space (target system),
         * RotationFromAxis() returns the rotation Euler angles (ex : rotation.x, rotation.y, rotation.z) to apply
         * to something in order to rotate it from its local system to the given target system
         * Note: axis1, axis2 and axis3 are normalized during this operation
         * @param axis1 defines the first axis
         * @param axis2 defines the second axis
         * @param axis3 defines the third axis
         * @returns a new Vector3
         */
        static RotationFromAxis(axis1, axis2, axis3) {
            let rotation = Vector3.Zero();
            Vector3.RotationFromAxisToRef(axis1, axis2, axis3, rotation);
            return rotation;
        }
        /**
         * The same than RotationFromAxis but updates the given ref Vector3 parameter instead of returning a new Vector3
         * @param axis1 defines the first axis
         * @param axis2 defines the second axis
         * @param axis3 defines the third axis
         * @param ref defines the Vector3 where to store the result
         */
        static RotationFromAxisToRef(axis1, axis2, axis3, ref) {
            let quat = MathTmp.Quaternion[0];
            Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
            quat.toEulerAnglesToRef(ref);
        }
    }
    FIREFLYX.Vector3 = Vector3;
    //Vector4 class created for EulerAngle class conversion to Quaternion
    class Vector4 {
        /**
         * Creates a Vector4 object from the given floats.
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(x, y, z, w) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.w = w;
        }
        /**
         * Returns the string with the Vector4 coordinates.
         */
        toString() {
            return "{X: " + this.x + " Y:" + this.y + " Z:" + this.z + " W:" + this.w + "}";
        }
        /**
         * Returns the string "Vector4".
         */
        getClassName() {
            return "Vector4";
        }
        /**
         * Returns the Vector4 hash code.
         */
        getHashCode() {
            let hash = this.x || 0;
            hash = (hash * 397) ^ (this.y || 0);
            hash = (hash * 397) ^ (this.z || 0);
            hash = (hash * 397) ^ (this.w || 0);
            return hash;
        }
        // Operators
        /**
         * Returns a new array populated with 4 elements : the Vector4 coordinates.
         */
        asArray() {
            let result = new Array();
            this.toArray(result, 0);
            return result;
        }
        /**
         * Populates the given array from the given index with the Vector4 coordinates.
         * Returns the Vector4.
         */
        toArray(array, index) {
            if (index === undefined) {
                index = 0;
            }
            array[index] = this.x;
            array[index + 1] = this.y;
            array[index + 2] = this.z;
            array[index + 3] = this.w;
            return this;
        }
        /**
         * Adds the given vector to the current Vector4.
         * Returns the updated Vector4.
         */
        addInPlace(otherVector) {
            this.x += otherVector.x;
            this.y += otherVector.y;
            this.z += otherVector.z;
            this.w += otherVector.w;
            return this;
        }
        /**
         * Returns a new Vector4 as the result of the addition of the current Vector4 and the given one.
         */
        add(otherVector) {
            return new Vector4(this.x + otherVector.x, this.y + otherVector.y, this.z + otherVector.z, this.w + otherVector.w);
        }
        /**
         * Updates the given vector "result" with the result of the addition of the current Vector4 and the given one.
         * Returns the current Vector4.
         */
        addToRef(otherVector, result) {
            result.x = this.x + otherVector.x;
            result.y = this.y + otherVector.y;
            result.z = this.z + otherVector.z;
            result.w = this.w + otherVector.w;
            return this;
        }
        /**
         * Subtract in place the given vector from the current Vector4.
         * Returns the updated Vector4.
         */
        subtractInPlace(otherVector) {
            this.x -= otherVector.x;
            this.y -= otherVector.y;
            this.z -= otherVector.z;
            this.w -= otherVector.w;
            return this;
        }
        /**
         * Returns a new Vector4 with the result of the subtraction of the given vector from the current Vector4.
         */
        subtract(otherVector) {
            return new Vector4(this.x - otherVector.x, this.y - otherVector.y, this.z - otherVector.z, this.w - otherVector.w);
        }
        /**
         * Sets the given vector "result" with the result of the subtraction of the given vector from the current Vector4.
         * Returns the current Vector4.
         */
        subtractToRef(otherVector, result) {
            result.x = this.x - otherVector.x;
            result.y = this.y - otherVector.y;
            result.z = this.z - otherVector.z;
            result.w = this.w - otherVector.w;
            return this;
        }
        /**
         * Returns a new Vector4 set with the result of the subtraction of the given floats from the current Vector4 coordinates.
         */
        subtractFromFloats(x, y, z, w) {
            return new Vector4(this.x - x, this.y - y, this.z - z, this.w - w);
        }
        /**
         * Sets the given vector "result" set with the result of the subtraction of the given floats from the current Vector4 coordinates.
         * Returns the current Vector4.
         */
        subtractFromFloatsToRef(x, y, z, w, result) {
            result.x = this.x - x;
            result.y = this.y - y;
            result.z = this.z - z;
            result.w = this.w - w;
            return this;
        }
        /**
         * Returns a new Vector4 set with the current Vector4 negated coordinates.
         */
        negate() {
            return new Vector4(-this.x, -this.y, -this.z, -this.w);
        }
        /**
         * Multiplies the current Vector4 coordinates by scale (float).
         * Returns the updated Vector4.
         */
        scaleInPlace(scale) {
            this.x *= scale;
            this.y *= scale;
            this.z *= scale;
            this.w *= scale;
            return this;
        }
        /**
         * Returns a new Vector4 set with the current Vector4 coordinates multiplied by scale (float).
         */
        scale(scale) {
            return new Vector4(this.x * scale, this.y * scale, this.z * scale, this.w * scale);
        }
        /**
         * Sets the given vector "result" with the current Vector4 coordinates multiplied by scale (float).
         * Returns the current Vector4.
         */
        scaleToRef(scale, result) {
            result.x = this.x * scale;
            result.y = this.y * scale;
            result.z = this.z * scale;
            result.w = this.w * scale;
            return this;
        }
        /**
         * Scale the current Vector4 values by a factor and add the result to a given Vector4
         * @param scale defines the scale factor
         * @param result defines the Vector4 object where to store the result
         * @returns the unmodified current Vector4
         */
        scaleAndAddToRef(scale, result) {
            result.x += this.x * scale;
            result.y += this.y * scale;
            result.z += this.z * scale;
            result.w += this.w * scale;
            return this;
        }
        /**
         * Boolean : True if the current Vector4 coordinates are stricly equal to the given ones.
         */
        equals(otherVector) {
            return otherVector && this.x === otherVector.x && this.y === otherVector.y && this.z === otherVector.z && this.w === otherVector.w;
        }
        /**
         * Boolean : True if the current Vector4 coordinates are each beneath the distance "epsilon" from the given vector ones.
         */
        equalsWithEpsilon(otherVector, _epsilon = FIREFLYX.epsilon) {
            return otherVector
                && FIREFLYX.Scalar.WithinEpsilon(this.x, otherVector.x, _epsilon)
                && FIREFLYX.Scalar.WithinEpsilon(this.y, otherVector.y, _epsilon)
                && FIREFLYX.Scalar.WithinEpsilon(this.z, otherVector.z, _epsilon)
                && FIREFLYX.Scalar.WithinEpsilon(this.w, otherVector.w, _epsilon);
        }
        /**
         * Boolean : True if the given floats are strictly equal to the current Vector4 coordinates.
         */
        equalsToFloats(x, y, z, w) {
            return this.x === x && this.y === y && this.z === z && this.w === w;
        }
        /**
         * Multiplies in place the current Vector4 by the given one.
         * Returns the updated Vector4.
         */
        multiplyInPlace(otherVector) {
            this.x *= otherVector.x;
            this.y *= otherVector.y;
            this.z *= otherVector.z;
            this.w *= otherVector.w;
            return this;
        }
        /**
         * Returns a new Vector4 set with the multiplication result of the current Vector4 and the given one.
         */
        multiply(otherVector) {
            return new Vector4(this.x * otherVector.x, this.y * otherVector.y, this.z * otherVector.z, this.w * otherVector.w);
        }
        /**
         * Updates the given vector "result" with the multiplication result of the current Vector4 and the given one.
         * Returns the current Vector4.
         */
        multiplyToRef(otherVector, result) {
            result.x = this.x * otherVector.x;
            result.y = this.y * otherVector.y;
            result.z = this.z * otherVector.z;
            result.w = this.w * otherVector.w;
            return this;
        }
        /**
         * Returns a new Vector4 set with the multiplication result of the given floats and the current Vector4 coordinates.
         */
        multiplyByFloats(x, y, z, w) {
            return new Vector4(this.x * x, this.y * y, this.z * z, this.w * w);
        }
        /**
         * Returns a new Vector4 set with the division result of the current Vector4 by the given one.
         */
        divide(otherVector) {
            return new Vector4(this.x / otherVector.x, this.y / otherVector.y, this.z / otherVector.z, this.w / otherVector.w);
        }
        /**
         * Updates the given vector "result" with the division result of the current Vector4 by the given one.
         * Returns the current Vector4.
         */
        divideToRef(otherVector, result) {
            result.x = this.x / otherVector.x;
            result.y = this.y / otherVector.y;
            result.z = this.z / otherVector.z;
            result.w = this.w / otherVector.w;
            return this;
        }
        /**
         * Divides the current Vector3 coordinates by the given ones.
         * @returns the updated Vector3.
         */
        divideInPlace(otherVector) {
            return this.divideToRef(otherVector, this);
        }
        /**
         * Updates the Vector4 coordinates with the minimum values between its own and the given vector ones
         * @param other defines the second operand
         * @returns the current updated Vector4
         */
        minimizeInPlace(other) {
            if (other.x < this.x)
                this.x = other.x;
            if (other.y < this.y)
                this.y = other.y;
            if (other.z < this.z)
                this.z = other.z;
            if (other.w < this.w)
                this.w = other.w;
            return this;
        }
        /**
         * Updates the Vector4 coordinates with the maximum values between its own and the given vector ones
         * @param other defines the second operand
         * @returns the current updated Vector4
         */
        maximizeInPlace(other) {
            if (other.x > this.x)
                this.x = other.x;
            if (other.y > this.y)
                this.y = other.y;
            if (other.z > this.z)
                this.z = other.z;
            if (other.w > this.w)
                this.w = other.w;
            return this;
        }
        /**
         * Gets a new Vector4 from current Vector4 floored values
         * @returns a new Vector4
         */
        floor() {
            return new Vector4(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z), Math.floor(this.w));
        }
        /**
         * Gets a new Vector4 from current Vector3 floored values
         * @returns a new Vector4
         */
        fract() {
            return new Vector4(this.x - Math.floor(this.x), this.y - Math.floor(this.y), this.z - Math.floor(this.z), this.w - Math.floor(this.w));
        }
        // Properties
        /**
         * Returns the Vector4 length (float).
         */
        length() {
            return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        }
        /**
         * Returns the Vector4 squared length (float).
         */
        lengthSquared() {
            return (this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        }
        // Methods
        /**
         * Normalizes in place the Vector4.
         * Returns the updated Vector4.
         */
        normalize() {
            let len = this.length();
            if (len === 0)
                return this;
            let num = 1.0 / len;
            this.x *= num;
            this.y *= num;
            this.z *= num;
            this.w *= num;
            return this;
        }
        /**
         * Returns a new Vector3 from the Vector4 (x, y, z) coordinates.
         */
        toVector3() {
            return new Vector3(this.x, this.y, this.z);
        }
        /**
         * Returns a new Vector4 copied from the current one.
         */
        clone() {
            return new Vector4(this.x, this.y, this.z, this.w);
        }
        /**
         * Updates the current Vector4 with the given one coordinates.
         * Returns the updated Vector4.
         */
        copyFrom(source) {
            this.x = source.x;
            this.y = source.y;
            this.z = source.z;
            this.w = source.w;
            return this;
        }
        /**
         * Updates the current Vector4 coordinates with the given floats.
         * Returns the updated Vector4.
         */
        copyFromFloats(x, y, z, w) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.w = w;
            return this;
        }
        /**
         * Updates the current Vector4 coordinates with the given floats.
         * Returns the updated Vector4.
         */
        set(x, y, z, w) {
            return this.copyFromFloats(x, y, z, w);
        }
        // Statics
        /**
         * Returns a new Vector4 set from the starting index of the given array.
         */
        static FromArray(array, offset) {
            if (!offset) {
                offset = 0;
            }
            return new Vector4(array[offset], array[offset + 1], array[offset + 2], array[offset + 3]);
        }
        /**
         * Updates the given vector "result" from the starting index of the given array.
         */
        static FromArrayToRef(array, offset, result) {
            result.x = array[offset];
            result.y = array[offset + 1];
            result.z = array[offset + 2];
            result.w = array[offset + 3];
        }
        /**
         * Updates the given vector "result" from the starting index of the given Float32Array.
         */
        static FromFloatArrayToRef(array, offset, result) {
            Vector4.FromArrayToRef(array, offset, result);
        }
        /**
         * Updates the given vector "result" coordinates from the given floats.
         */
        static FromFloatsToRef(x, y, z, w, result) {
            result.x = x;
            result.y = y;
            result.z = z;
            result.w = w;
        }
        /**
         * Returns a new Vector4 set to (0.0, 0.0, 0.0, 0.0)
         */
        static Zero() {
            return new Vector4(0.0, 0.0, 0.0, 0.0);
        }
        /**
         * Returns a new Vector4 set to (1.0, 1.0, 1.0, 1.0)
         */
        static One() {
            return new Vector4(1.0, 1.0, 1.0, 1.0);
        }
        /**
         * Returns a new normalized Vector4 from the given one.
         */
        static Normalize(vector) {
            let result = Vector4.Zero();
            Vector4.NormalizeToRef(vector, result);
            return result;
        }
        /**
         * Updates the given vector "result" from the normalization of the given one.
         */
        static NormalizeToRef(vector, result) {
            result.copyFrom(vector);
            result.normalize();
        }
        static Minimize(left, right) {
            let min = left.clone();
            min.minimizeInPlace(right);
            return min;
        }
        static Maximize(left, right) {
            let max = left.clone();
            max.maximizeInPlace(right);
            return max;
        }
        /**
         * Returns the distance (float) between the vectors "value1" and "value2".
         */
        static Distance(value1, value2) {
            return Math.sqrt(Vector4.DistanceSquared(value1, value2));
        }
        /**
         * Returns the squared distance (float) between the vectors "value1" and "value2".
         */
        static DistanceSquared(value1, value2) {
            let x = value1.x - value2.x;
            let y = value1.y - value2.y;
            let z = value1.z - value2.z;
            let w = value1.w - value2.w;
            return (x * x) + (y * y) + (z * z) + (w * w);
        }
        /**
         * Returns a new Vector4 located at the center between the vectors "value1" and "value2".
         */
        static Center(value1, value2) {
            let center = value1.add(value2);
            center.scaleInPlace(0.5);
            return center;
        }
        /**
         * Returns a new Vector4 set with the result of the normal transformation by the given matrix of the given vector.
         * This methods computes transformed normalized direction vectors only.
         */
        static TransformNormal(vector, transformation) {
            let result = Vector4.Zero();
            Vector4.TransformNormalToRef(vector, transformation, result);
            return result;
        }
        /**
         * Sets the given vector "result" with the result of the normal transformation by the given matrix of the given vector.
         * This methods computes transformed normalized direction vectors only.
         */
        static TransformNormalToRef(vector, transformation, result) {
            let x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
            let y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
            let z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
            result.x = x;
            result.y = y;
            result.z = z;
            result.w = vector.w;
        }
        /**
         * Sets the given vector "result" with the result of the normal transformation by the given matrix of the given floats (x, y, z, w).
         * This methods computes transformed normalized direction vectors only.
         */
        static TransformNormalFromFloatsToRef(x, y, z, w, transformation, result) {
            result.x = (x * transformation.m[0]) + (y * transformation.m[4]) + (z * transformation.m[8]);
            result.y = (x * transformation.m[1]) + (y * transformation.m[5]) + (z * transformation.m[9]);
            result.z = (x * transformation.m[2]) + (y * transformation.m[6]) + (z * transformation.m[10]);
            result.w = w;
        }
    }
    FIREFLYX.Vector4 = Vector4;
    class Size {
        /**
         * Creates a Size object from the given width and height (floats).
         */
        constructor(width, height) {
            this.width = width;
            this.height = height;
        }
        // Returns a string with the Size width and height.
        toString() {
            return `{W: ${this.width}, H: ${this.height}}`;
        }
        /**
         * Returns the string "Size"
         */
        getClassName() {
            return "Size";
        }
        /**
         * Returns the Size hash code.
         */
        getHashCode() {
            let hash = this.width || 0;
            hash = (hash * 397) ^ (this.height || 0);
            return hash;
        }
        /**
         * Updates the current size from the given one.
         * Returns the updated Size.
         */
        copyFrom(src) {
            this.width = src.width;
            this.height = src.height;
        }
        /**
         * Updates in place the current Size from the given floats.
         * Returns the updated Size.
         */
        copyFromFloats(width, height) {
            this.width = width;
            this.height = height;
            return this;
        }
        /**
         * Updates in place the current Size from the given floats.
         * Returns the updated Size.
         */
        set(width, height) {
            return this.copyFromFloats(width, height);
        }
        /**
         * Returns a new Size set with the multiplication result of the current Size and the given floats.
         */
        multiplyByFloats(w, h) {
            return new Size(this.width * w, this.height * h);
        }
        /**
         * Returns a new Size copied from the given one.
         */
        clone() {
            return new Size(this.width, this.height);
        }
        /**
         * Boolean : True if the current Size and the given one width and height are strictly equal.
         */
        equals(other) {
            if (!other) {
                return false;
            }
            return (this.width === other.width) && (this.height === other.height);
        }
        /**
         * Returns the surface of the Size : width * height (float).
         */
        get surface() {
            return this.width * this.height;
        }
        /**
         * Returns a new Size set to (0.0, 0.0)
         */
        static Zero() {
            return new Size(0.0, 0.0);
        }
        /**
         * Returns a new Size set as the addition result of the current Size and the given one.
         */
        add(otherSize) {
            let r = new Size(this.width + otherSize.width, this.height + otherSize.height);
            return r;
        }
        /**
         * Returns a new Size set as the subtraction result of  the given one from the current Size.
         */
        subtract(otherSize) {
            let r = new Size(this.width - otherSize.width, this.height - otherSize.height);
            return r;
        }
        /**
         * Returns a new Size set at the linear interpolation "amount" between "start" and "end".
         */
        static Lerp(start, end, amount) {
            let w = start.width + ((end.width - start.width) * amount);
            let h = start.height + ((end.height - start.height) * amount);
            return new Size(w, h);
        }
    }
    FIREFLYX.Size = Size;
    /**
     * Class used to store quaternion data
     * @see https://en.wikipedia.org/wiki/Quaternion
     * @see http://doc.babylonjs.com/features/position,_rotation,_scaling
     */
    class Quaternion {
        /**
         * Creates a new Quaternion from the given floats
         * @param x defines the first component (0 by default)
         * @param y defines the second component (0 by default)
         * @param z defines the third component (0 by default)
         * @param w defines the fourth component (1.0 by default)
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(
        /** defines the first component (0 by default) */
        x = 0.0, 
        /** defines the second component (0 by default) */
        y = 0.0, 
        /** defines the third component (0 by default) */
        z = 0.0, 
        /** defines the fourth component (1.0 by default) */
        w = 1.0) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.w = w;
        }
        /**
         * Gets a string representation for the current quaternion
         * @returns a string with the Quaternion coordinates
         */
        toString() {
            return "{X: " + this.x + " Y:" + this.y + " Z:" + this.z + " W:" + this.w + "}";
        }
        /**
         * Gets the class name of the quaternion
         * @returns the string "Quaternion"
         */
        getClassName() {
            return "Quaternion";
        }
        /**
         * Gets a hash code for this quaternion
         * @returns the quaternion hash code
         */
        getHashCode() {
            let hash = this.x || 0;
            hash = (hash * 397) ^ (this.y || 0);
            hash = (hash * 397) ^ (this.z || 0);
            hash = (hash * 397) ^ (this.w || 0);
            return hash;
        }
        /**
         * Copy the quaternion to an array
         * @returns a new array populated with 4 elements from the quaternion coordinates
         */
        asArray() {
            return [this.x, this.y, this.z, this.w];
        }
        /**
         * Check if two quaternions are equals
         * @param otherQuaternion defines the second operand
         * @return true if the current quaternion and the given one coordinates are strictly equals
         */
        equals(otherQuaternion) {
            return otherQuaternion && this.x === otherQuaternion.x && this.y === otherQuaternion.y && this.z === otherQuaternion.z && this.w === otherQuaternion.w;
        }
        /**
         * Clone the current quaternion
         * @returns a new quaternion copied from the current one
         */
        clone() {
            return new Quaternion(this.x, this.y, this.z, this.w);
        }
        /**
         * Copy a quaternion to the current one
         * @param other defines the other quaternion
         * @returns the updated current quaternion
         */
        copyFrom(other) {
            this.x = other.x;
            this.y = other.y;
            this.z = other.z;
            this.w = other.w;
            return this;
        }
        /**
         * Updates the current quaternion with the given float coordinates
         * @param x defines the x coordinate
         * @param y defines the y coordinate
         * @param z defines the z coordinate
         * @param w defines the w coordinate
         * @returns the updated current quaternion
         */
        copyFromFloats(x, y, z, w) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.w = w;
            return this;
        }
        /**
         * Updates the current quaternion from the given float coordinates
         * @param x defines the x coordinate
         * @param y defines the y coordinate
         * @param z defines the z coordinate
         * @param w defines the w coordinate
         * @returns the updated current quaternion
         */
        set(x, y, z, w) {
            return this.copyFromFloats(x, y, z, w);
        }
        /**
         * Adds two quaternions
         * @param other defines the second operand
         * @returns a new quaternion as the addition result of the given one and the current quaternion
         */
        add(other) {
            return new Quaternion(this.x + other.x, this.y + other.y, this.z + other.z, this.w + other.w);
        }
        /**
         * Add a quaternion to the current one
         * @param other defines the quaternion to add
         * @returns the current quaternion
         */
        addInPlace(other) {
            this.x += other.x;
            this.y += other.y;
            this.z += other.z;
            this.w += other.w;
            return this;
        }
        /**
         * Subtract two quaternions
         * @param other defines the second operand
         * @returns a new quaternion as the subtraction result of the given one from the current one
         */
        subtract(other) {
            return new Quaternion(this.x - other.x, this.y - other.y, this.z - other.z, this.w - other.w);
        }
        /**
         * Multiplies the current quaternion by a scale factor
         * @param value defines the scale factor
         * @returns a new quaternion set by multiplying the current quaternion coordinates by the float "scale"
         */
        scale(value) {
            return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
        }
        /**
         * Scale the current quaternion values by a factor and stores the result to a given quaternion
         * @param scale defines the scale factor
         * @param result defines the Quaternion object where to store the result
         * @returns the unmodified current quaternion
         */
        scaleToRef(scale, result) {
            result.x = this.x * scale;
            result.y = this.y * scale;
            result.z = this.z * scale;
            result.w = this.w * scale;
            return this;
        }
        /**
         * Multiplies in place the current quaternion by a scale factor
         * @param value defines the scale factor
         * @returns the current modified quaternion
         */
        scaleInPlace(value) {
            this.x *= value;
            this.y *= value;
            this.z *= value;
            this.w *= value;
            return this;
        }
        /**
         * Scale the current quaternion values by a factor and add the result to a given quaternion
         * @param scale defines the scale factor
         * @param result defines the Quaternion object where to store the result
         * @returns the unmodified current quaternion
         */
        scaleAndAddToRef(scale, result) {
            result.x += this.x * scale;
            result.y += this.y * scale;
            result.z += this.z * scale;
            result.w += this.w * scale;
            return this;
        }
        /**
         * Multiplies two quaternions
         * @param q1 defines the second operand
         * @returns a new quaternion set as the multiplication result of the current one with the given one "q1"
         */
        multiply(q1) {
            let result = new Quaternion(0, 0, 0, 1.0);
            this.multiplyToRef(q1, result);
            return result;
        }
        /**
         * Sets the given "result" as the the multiplication result of the current one with the given one "q1"
         * @param q1 defines the second operand
         * @param result defines the target quaternion
         * @returns the current quaternion
         */
        multiplyToRef(q1, result) {
            let x = this.x * q1.w + this.y * q1.z - this.z * q1.y + this.w * q1.x;
            let y = -this.x * q1.z + this.y * q1.w + this.z * q1.x + this.w * q1.y;
            let z = this.x * q1.y - this.y * q1.x + this.z * q1.w + this.w * q1.z;
            let w = -this.x * q1.x - this.y * q1.y - this.z * q1.z + this.w * q1.w;
            result.copyFromFloats(x, y, z, w);
            return this;
        }
        /**
         * Updates the current quaternion with the multiplication of itself with the given one "q1"
         * @param q1 defines the second operand
         * @returns the currentupdated quaternion
         */
        multiplyInPlace(q1) {
            this.multiplyToRef(q1, this);
            return this;
        }
        /**
         * Conjugates (1-q) the current quaternion and stores the result in the given quaternion
         * @param ref defines the target quaternion
         * @returns the current quaternion
         */
        conjugateToRef(ref) {
            ref.copyFromFloats(-this.x, -this.y, -this.z, this.w);
            return this;
        }
        /**
         * Conjugates in place (1-q) the current quaternion
         * @returns the current updated quaternion
         */
        conjugateInPlace() {
            this.x *= -1;
            this.y *= -1;
            this.z *= -1;
            return this;
        }
        /**
         * Conjugates in place (1-q) the current quaternion
         * @returns a new quaternion
         */
        conjugate() {
            let result = new Quaternion(-this.x, -this.y, -this.z, this.w);
            return result;
        }
        /**
         * Gets length of current quaternion
         * @returns the quaternion length (float)
         */
        length() {
            return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z) + (this.w * this.w));
        }
        /**
         * Normalize in place the current quaternion
         * @returns the current updated quaternion
         */
        normalize() {
            let length = 1.0 / this.length();
            this.x *= length;
            this.y *= length;
            this.z *= length;
            this.w *= length;
            return this;
        }
        /**
         * Returns a new Vector3 set with the Euler angles translated from the current quaternion
         * @param order is a reserved parameter and is ignore for now
         * @returns a new Vector3 containing the Euler angles
         */
        toEulerAngles(order = "YZX") {
            let result = Vector3.Zero();
            this.toEulerAnglesToRef(result, order);
            return result;
        }
        /**
         * Sets the given vector3 "result" with the Euler angles translated from the current quaternion
         * @param result defines the vector which will be filled with the Euler angles
         * @param order is a reserved parameter and is ignore for now
         * @returns the current unchanged quaternion
         */
        toEulerAnglesToRef(result, order = "YZX") {
            let qz = this.z;
            let qx = this.x;
            let qy = this.y;
            let qw = this.w;
            let sqw = qw * qw;
            let sqz = qz * qz;
            let sqx = qx * qx;
            let sqy = qy * qy;
            let zAxisY = qy * qz - qx * qw;
            let limit = .4999999;
            if (zAxisY < -limit) {
                result.y = 2 * Math.atan2(qy, qw);
                result.x = Math.PI / 2;
                result.z = 0;
            }
            else if (zAxisY > limit) {
                result.y = 2 * Math.atan2(qy, qw);
                result.x = -Math.PI / 2;
                result.z = 0;
            }
            else {
                result.z = Math.atan2(2.0 * (qx * qy + qz * qw), (-sqz - sqx + sqy + sqw));
                result.x = Math.asin(-2.0 * (qz * qy - qx * qw));
                result.y = Math.atan2(2.0 * (qz * qx + qy * qw), (sqz - sqx - sqy + sqw));
            }
            return this;
        }
        /**
         * Updates the given rotation matrix with the current quaternion values
         * @param result defines the target matrix
         * @returns the current unchanged quaternion
         */
        toRotationMatrix(result) {
            let xx = this.x * this.x;
            let yy = this.y * this.y;
            let zz = this.z * this.z;
            let xy = this.x * this.y;
            let zw = this.z * this.w;
            let zx = this.z * this.x;
            let yw = this.y * this.w;
            let yz = this.y * this.z;
            let xw = this.x * this.w;
            result.m[0] = 1.0 - (2.0 * (yy + zz));
            result.m[1] = 2.0 * (xy + zw);
            result.m[2] = 2.0 * (zx - yw);
            result.m[3] = 0;
            result.m[4] = 2.0 * (xy - zw);
            result.m[5] = 1.0 - (2.0 * (zz + xx));
            result.m[6] = 2.0 * (yz + xw);
            result.m[7] = 0;
            result.m[8] = 2.0 * (zx + yw);
            result.m[9] = 2.0 * (yz - xw);
            result.m[10] = 1.0 - (2.0 * (yy + xx));
            result.m[11] = 0;
            result.m[12] = 0;
            result.m[13] = 0;
            result.m[14] = 0;
            result.m[15] = 1.0;
            result._markAsUpdated();
            return this;
        }
        /**
         * Updates the current quaternion from the given rotation matrix values
         * @param matrix defines the source matrix
         * @returns the current updated quaternion
         */
        fromRotationMatrix(matrix) {
            Quaternion.FromRotationMatrixToRef(matrix, this);
            return this;
        }
        // Statics
        /**
         * Creates a new quaternion from a rotation matrix
         * @param matrix defines the source matrix
         * @returns a new quaternion created from the given rotation matrix values
         */
        static FromRotationMatrix(matrix) {
            let result = new Quaternion();
            Quaternion.FromRotationMatrixToRef(matrix, result);
            return result;
        }
        /**
         * Updates the given quaternion with the given rotation matrix values
         * @param matrix defines the source matrix
         * @param result defines the target quaternion
         */
        static FromRotationMatrixToRef(matrix, result) {
            let data = matrix.m;
            let m11 = data[0], m12 = data[4], m13 = data[8];
            let m21 = data[1], m22 = data[5], m23 = data[9];
            let m31 = data[2], m32 = data[6], m33 = data[10];
            let trace = m11 + m22 + m33;
            let s;
            if (trace > 0) {
                s = 0.5 / Math.sqrt(trace + 1.0);
                result.w = 0.25 / s;
                result.x = (m32 - m23) * s;
                result.y = (m13 - m31) * s;
                result.z = (m21 - m12) * s;
            }
            else if (m11 > m22 && m11 > m33) {
                s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
                result.w = (m32 - m23) / s;
                result.x = 0.25 * s;
                result.y = (m12 + m21) / s;
                result.z = (m13 + m31) / s;
            }
            else if (m22 > m33) {
                s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
                result.w = (m13 - m31) / s;
                result.x = (m12 + m21) / s;
                result.y = 0.25 * s;
                result.z = (m23 + m32) / s;
            }
            else {
                s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
                result.w = (m21 - m12) / s;
                result.x = (m13 + m31) / s;
                result.y = (m23 + m32) / s;
                result.z = 0.25 * s;
            }
        }
        /**
         * Returns the dot product (float) between the quaternions "left" and "right"
         * @param left defines the left operand
         * @param right defines the right operand
         * @returns the dot product
         */
        static Dot(left, right) {
            return (left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w);
        }
        /**
         * Checks if the two quaternions are close to each other
         * @param quat0 defines the first quaternion to check
         * @param quat1 defines the second quaternion to check
         * @returns true if the two quaternions are close to each other
         */
        static AreClose(quat0, quat1) {
            let dot = Quaternion.Dot(quat0, quat1);
            return dot >= 0;
        }
        /**
         * Creates an empty quaternion
         * @returns a new quaternion set to (0.0, 0.0, 0.0)
         */
        static Zero() {
            return new Quaternion(0.0, 0.0, 0.0, 0.0);
        }
        /**
         * Inverse a given quaternion
         * @param q defines the source quaternion
         * @returns a new quaternion as the inverted current quaternion
         */
        static Inverse(q) {
            return new Quaternion(-q.x, -q.y, -q.z, q.w);
        }
        /**
         * Inverse a given quaternion
         * @param q defines the source quaternion
         */
        static InverseSelf(q) {
            q.set(-q.x, -q.y, -q.z, q.w);
        }
        /**
         * Creates an identity quaternion
         * @returns the identity quaternion
         */
        static Identity() {
            return new Quaternion(0.0, 0.0, 0.0, 1.0);
        }
        /**
         * Gets a boolean indicating if the given quaternion is identity
         * @param quaternion defines the quaternion to check
         * @returns true if the quaternion is identity
         */
        static IsIdentity(quaternion) {
            return quaternion && quaternion.x === 0 && quaternion.y === 0 && quaternion.z === 0 && quaternion.w === 1;
        }
        /**
         * Creates a quaternion from a rotation around an axis
         * @param axis defines the axis to use
         * @param angle defines the angle to use
         * @returns a new quaternion created from the given axis (Vector3) and angle in radians (float)
         */
        static RotationAxis(axis, angle) {
            return Quaternion.RotationAxisToRef(axis, angle, new Quaternion());
        }
        /**
         * Creates a rotation around an axis and stores it into the given quaternion
         * @param axis defines the axis to use
         * @param angle defines the angle to use
         * @param result defines the target quaternion
         * @returns the target quaternion
         */
        static RotationAxisToRef(axis, angle, result) {
            let sin = Math.sin(angle / 2);
            axis.normalize();
            result.w = Math.cos(angle / 2);
            result.x = axis.x * sin;
            result.y = axis.y * sin;
            result.z = axis.z * sin;
            return result;
        }
        /**
         * Creates a new quaternion from data stored into an array
         * @param array defines the data source
         * @param offset defines the offset in the source array where the data starts
         * @returns a new quaternion
         */
        static FromArray(array, offset) {
            if (!offset) {
                offset = 0;
            }
            return new Quaternion(array[offset], array[offset + 1], array[offset + 2], array[offset + 3]);
        }
        /**
         * Creates a new quaternion from the given Euler float angles (y, x, z)
         * @param yaw defines the rotation around Y axis
         * @param pitch defines the rotation around X axis
         * @param roll defines the rotation around Z axis
         * @returns the new quaternion
         */
        static RotationYawPitchRoll(yaw, pitch, roll) {
            let q = new Quaternion();
            Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, q);
            return q;
        }
        /**
         * Creates a new rotation from the given Euler float angles (y, x, z) and stores it in the target quaternion
         * @param yaw defines the rotation around Y axis
         * @param pitch defines the rotation around X axis
         * @param roll defines the rotation around Z axis
         * @param result defines the target quaternion
         */
        static RotationYawPitchRollToRef(yaw, pitch, roll, result) {
            // Produces a quaternion from Euler angles in the z-y-x orientation (Tait-Bryan angles)
            let halfRoll = roll * 0.5;
            let halfPitch = pitch * 0.5;
            let halfYaw = yaw * 0.5;
            let sinRoll = Math.sin(halfRoll);
            let cosRoll = Math.cos(halfRoll);
            let sinPitch = Math.sin(halfPitch);
            let cosPitch = Math.cos(halfPitch);
            let sinYaw = Math.sin(halfYaw);
            let cosYaw = Math.cos(halfYaw);
            result.x = (cosYaw * sinPitch * cosRoll) + (sinYaw * cosPitch * sinRoll);
            result.y = (sinYaw * cosPitch * cosRoll) - (cosYaw * sinPitch * sinRoll);
            result.z = (cosYaw * cosPitch * sinRoll) - (sinYaw * sinPitch * cosRoll);
            result.w = (cosYaw * cosPitch * cosRoll) + (sinYaw * sinPitch * sinRoll);
        }
        /**
         * Creates a new quaternion from the given Euler float angles expressed in z-x-z orientation
         * @param alpha defines the rotation around first axis
         * @param beta defines the rotation around second axis
         * @param gamma defines the rotation around third axis
         * @returns the new quaternion
         */
        static RotationAlphaBetaGamma(alpha, beta, gamma) {
            let result = new Quaternion();
            Quaternion.RotationAlphaBetaGammaToRef(alpha, beta, gamma, result);
            return result;
        }
        /**
         * Creates a new quaternion from the given Euler float angles expressed in z-x-z orientation and stores it in the target quaternion
         * @param alpha defines the rotation around first axis
         * @param beta defines the rotation around second axis
         * @param gamma defines the rotation around third axis
         * @param result defines the target quaternion
         */
        static RotationAlphaBetaGammaToRef(alpha, beta, gamma, result) {
            // Produces a quaternion from Euler angles in the z-x-z orientation
            let halfGammaPlusAlpha = (gamma + alpha) * 0.5;
            let halfGammaMinusAlpha = (gamma - alpha) * 0.5;
            let halfBeta = beta * 0.5;
            result.x = Math.cos(halfGammaMinusAlpha) * Math.sin(halfBeta);
            result.y = Math.sin(halfGammaMinusAlpha) * Math.sin(halfBeta);
            result.z = Math.sin(halfGammaPlusAlpha) * Math.cos(halfBeta);
            result.w = Math.cos(halfGammaPlusAlpha) * Math.cos(halfBeta);
        }
        /**
         * Creates a new quaternion containing the rotation value to reach the target (axis1, axis2, axis3) orientation
         * as a rotated XYZ system (axis1, axis2 and axis3 are normalized during this operation)
         * @param axis1 defines the first axis
         * @param axis2 defines the second axis
         * @param axis3 defines the third axis
         * @returns the new quaternion
         */
        static RotationQuaternionFromAxis(axis1, axis2, axis3) {
            let quat = new Quaternion(0.0, 0.0, 0.0, 0.0);
            Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
            return quat;
        }
        /**
         * Creates a rotation value to reach the target (axis1, axis2, axis3) orientation as a rotated
         * XYZ system (axis1, axis2 and axis3 are normalized during this operation) and stores it in the target quaternion
         * @param axis1 defines the first axis
         * @param axis2 defines the second axis
         * @param axis3 defines the third axis
         * @param ref defines the target quaternion
         */
        static RotationQuaternionFromAxisToRef(axis1, axis2, axis3, ref) {
            let rotMat = MathTmp.Matrix[0];
            Matrix.FromXYZAxesToRef(axis1.normalize(), axis2.normalize(), axis3.normalize(), rotMat);
            Quaternion.FromRotationMatrixToRef(rotMat, ref);
        }
        /**
         * Interpolates between two quaternions
         * @param left defines first quaternion
         * @param right defines second quaternion
         * @param amount defines the gradient to use
         * @returns the new interpolated quaternion
         */
        static Slerp(left, right, amount) {
            let result = Quaternion.Identity();
            Quaternion.SlerpToRef(left, right, amount, result);
            return result;
        }
        /**
         * Interpolates between two quaternions and stores it into a target quaternion
         * @param left defines first quaternion
         * @param right defines second quaternion
         * @param amount defines the gradient to use
         * @param result defines the target quaternion
         */
        static SlerpToRef(left, right, amount, result) {
            let num2;
            let num3;
            let num4 = (((left.x * right.x) + (left.y * right.y)) + (left.z * right.z)) + (left.w * right.w);
            let flag = false;
            if (num4 < 0) {
                flag = true;
                num4 = -num4;
            }
            if (num4 > 0.999999) {
                num3 = 1 - amount;
                num2 = flag ? -amount : amount;
            }
            else {
                let num5 = Math.acos(num4);
                let num6 = (1.0 / Math.sin(num5));
                num3 = (Math.sin((1.0 - amount) * num5)) * num6;
                num2 = flag ? ((-Math.sin(amount * num5)) * num6) : ((Math.sin(amount * num5)) * num6);
            }
            result.x = (num3 * left.x) + (num2 * right.x);
            result.y = (num3 * left.y) + (num2 * right.y);
            result.z = (num3 * left.z) + (num2 * right.z);
            result.w = (num3 * left.w) + (num2 * right.w);
        }
        /**
         * Interpolate between two quaternions using Hermite interpolation
         * @param value1 defines first quaternion
         * @param tangent1 defines the incoming tangent
         * @param value2 defines second quaternion
         * @param tangent2 defines the outgoing tangent
         * @param amount defines the target quaternion
         * @returns the new interpolated quaternion
         */
        static Hermite(value1, tangent1, value2, tangent2, amount) {
            let squared = amount * amount;
            let cubed = amount * squared;
            let part1 = ((2.0 * cubed) - (3.0 * squared)) + 1.0;
            let part2 = (-2.0 * cubed) + (3.0 * squared);
            let part3 = (cubed - (2.0 * squared)) + amount;
            let part4 = cubed - squared;
            let x = (((value1.x * part1) + (value2.x * part2)) + (tangent1.x * part3)) + (tangent2.x * part4);
            let y = (((value1.y * part1) + (value2.y * part2)) + (tangent1.y * part3)) + (tangent2.y * part4);
            let z = (((value1.z * part1) + (value2.z * part2)) + (tangent1.z * part3)) + (tangent2.z * part4);
            let w = (((value1.w * part1) + (value2.w * part2)) + (tangent1.w * part3)) + (tangent2.w * part4);
            return new Quaternion(x, y, z, w);
        }
    }
    FIREFLYX.Quaternion = Quaternion;
    /**
     * Class used to store matrix data (4x4)
     */
    class Matrix {
        /**
         * Creates an empty matrix (filled with zeros)
         */
        constructor() {
            this._isIdentity = false;
            this._isIdentityDirty = true;
            /**
             * Gets or sets the internal data of the matrix
             */
            this.m = new Float32Array(16);
            this._markAsUpdated();
        }
        /** @hidden */
        _markAsUpdated() {
            this.updateFlag = Matrix._updateFlagSeed++;
            this._isIdentityDirty = true;
        }
        // Properties
        /**
         * Check if the current matrix is indentity
         * @param considerAsTextureMatrix defines if the current matrix must be considered as a texture matrix (3x2)
         * @returns true is the matrix is the identity matrix
         */
        isIdentity(considerAsTextureMatrix = false) {
            if (this._isIdentityDirty) {
                this._isIdentityDirty = false;
                if (this.m[0] !== 1.0 || this.m[5] !== 1.0 || this.m[15] !== 1.0) {
                    this._isIdentity = false;
                }
                else if (this.m[1] !== 0.0 || this.m[2] !== 0.0 || this.m[3] !== 0.0 ||
                    this.m[4] !== 0.0 || this.m[6] !== 0.0 || this.m[7] !== 0.0 ||
                    this.m[8] !== 0.0 || this.m[9] !== 0.0 || this.m[11] !== 0.0 ||
                    this.m[12] !== 0.0 || this.m[13] !== 0.0 || this.m[14] !== 0.0) {
                    this._isIdentity = false;
                }
                else {
                    this._isIdentity = true;
                }
                if (!considerAsTextureMatrix && this.m[10] !== 1.0) {
                    this._isIdentity = false;
                }
            }
            return this._isIdentity;
        }
        /**
         * Gets the determinant of the matrix
         * @returns the matrix determinant
         */
        determinant() {
            let temp1 = (this.m[10] * this.m[15]) - (this.m[11] * this.m[14]);
            let temp2 = (this.m[9] * this.m[15]) - (this.m[11] * this.m[13]);
            let temp3 = (this.m[9] * this.m[14]) - (this.m[10] * this.m[13]);
            let temp4 = (this.m[8] * this.m[15]) - (this.m[11] * this.m[12]);
            let temp5 = (this.m[8] * this.m[14]) - (this.m[10] * this.m[12]);
            let temp6 = (this.m[8] * this.m[13]) - (this.m[9] * this.m[12]);
            return ((((this.m[0] * (((this.m[5] * temp1) - (this.m[6] * temp2)) + (this.m[7] * temp3))) - (this.m[1] * (((this.m[4] * temp1) -
                (this.m[6] * temp4)) + (this.m[7] * temp5)))) + (this.m[2] * (((this.m[4] * temp2) - (this.m[5] * temp4)) + (this.m[7] * temp6)))) -
                (this.m[3] * (((this.m[4] * temp3) - (this.m[5] * temp5)) + (this.m[6] * temp6))));
        }
        // Methods
        /**
         * Returns the matrix as a Float32Array
         * @returns the matrix underlying array
         */
        toArray() {
            return this.m;
        }
        /**
         * Returns the matrix as a Float32Array
        * @returns the matrix underlying array.
        */
        asArray() {
            return this.toArray();
        }
        /**
         * Inverts the current matrix in place
         * @returns the current inverted matrix
         */
        invert() {
            this.invertToRef(this);
            return this;
        }
        /**
         * Sets all the matrix elements to zero
         * @returns the current matrix
         */
        reset() {
            for (let index = 0; index < 16; index++) {
                this.m[index] = 0.0;
            }
            this._markAsUpdated();
            return this;
        }
        /**
         * Adds the current matrix with a second one
         * @param other defines the matrix to add
         * @returns a new matrix as the addition of the current matrix and the given one
         */
        add(other) {
            let result = new Matrix();
            this.addToRef(other, result);
            return result;
        }
        /**
         * Sets the given matrix "result" to the addition of the current matrix and the given one
         * @param other defines the matrix to add
         * @param result defines the target matrix
         * @returns the current matrix
         */
        addToRef(other, result) {
            for (let index = 0; index < 16; index++) {
                result.m[index] = this.m[index] + other.m[index];
            }
            result._markAsUpdated();
            return this;
        }
        /**
         * Adds in place the given matrix to the current matrix
         * @param other defines the second operand
         * @returns the current updated matrix
         */
        addToSelf(other) {
            for (let index = 0; index < 16; index++) {
                this.m[index] += other.m[index];
            }
            this._markAsUpdated();
            return this;
        }
        /**
         * Sets the given matrix to the current inverted Matrix
         * @param other defines the target matrix
         * @returns the unmodified current matrix
         */
        invertToRef(other) {
            let l1 = this.m[0];
            let l2 = this.m[1];
            let l3 = this.m[2];
            let l4 = this.m[3];
            let l5 = this.m[4];
            let l6 = this.m[5];
            let l7 = this.m[6];
            let l8 = this.m[7];
            let l9 = this.m[8];
            let l10 = this.m[9];
            let l11 = this.m[10];
            let l12 = this.m[11];
            let l13 = this.m[12];
            let l14 = this.m[13];
            let l15 = this.m[14];
            let l16 = this.m[15];
            let l17 = (l11 * l16) - (l12 * l15);
            let l18 = (l10 * l16) - (l12 * l14);
            let l19 = (l10 * l15) - (l11 * l14);
            let l20 = (l9 * l16) - (l12 * l13);
            let l21 = (l9 * l15) - (l11 * l13);
            let l22 = (l9 * l14) - (l10 * l13);
            let l23 = ((l6 * l17) - (l7 * l18)) + (l8 * l19);
            let l24 = -(((l5 * l17) - (l7 * l20)) + (l8 * l21));
            let l25 = ((l5 * l18) - (l6 * l20)) + (l8 * l22);
            let l26 = -(((l5 * l19) - (l6 * l21)) + (l7 * l22));
            let l27 = 1.0 / ((((l1 * l23) + (l2 * l24)) + (l3 * l25)) + (l4 * l26));
            let l28 = (l7 * l16) - (l8 * l15);
            let l29 = (l6 * l16) - (l8 * l14);
            let l30 = (l6 * l15) - (l7 * l14);
            let l31 = (l5 * l16) - (l8 * l13);
            let l32 = (l5 * l15) - (l7 * l13);
            let l33 = (l5 * l14) - (l6 * l13);
            let l34 = (l7 * l12) - (l8 * l11);
            let l35 = (l6 * l12) - (l8 * l10);
            let l36 = (l6 * l11) - (l7 * l10);
            let l37 = (l5 * l12) - (l8 * l9);
            let l38 = (l5 * l11) - (l7 * l9);
            let l39 = (l5 * l10) - (l6 * l9);
            other.m[0] = l23 * l27;
            other.m[4] = l24 * l27;
            other.m[8] = l25 * l27;
            other.m[12] = l26 * l27;
            other.m[1] = -(((l2 * l17) - (l3 * l18)) + (l4 * l19)) * l27;
            other.m[5] = (((l1 * l17) - (l3 * l20)) + (l4 * l21)) * l27;
            other.m[9] = -(((l1 * l18) - (l2 * l20)) + (l4 * l22)) * l27;
            other.m[13] = (((l1 * l19) - (l2 * l21)) + (l3 * l22)) * l27;
            other.m[2] = (((l2 * l28) - (l3 * l29)) + (l4 * l30)) * l27;
            other.m[6] = -(((l1 * l28) - (l3 * l31)) + (l4 * l32)) * l27;
            other.m[10] = (((l1 * l29) - (l2 * l31)) + (l4 * l33)) * l27;
            other.m[14] = -(((l1 * l30) - (l2 * l32)) + (l3 * l33)) * l27;
            other.m[3] = -(((l2 * l34) - (l3 * l35)) + (l4 * l36)) * l27;
            other.m[7] = (((l1 * l34) - (l3 * l37)) + (l4 * l38)) * l27;
            other.m[11] = -(((l1 * l35) - (l2 * l37)) + (l4 * l39)) * l27;
            other.m[15] = (((l1 * l36) - (l2 * l38)) + (l3 * l39)) * l27;
            other._markAsUpdated();
            return this;
        }
        /**
         * Inserts the translation vector (using 3 floats) in the current matrix
         * @param x defines the 1st component of the translation
         * @param y defines the 2nd component of the translation
         * @param z defines the 3rd component of the translation
         * @returns the current updated matrix
         */
        setTranslationFromFloats(x, y, z) {
            this.m[12] = x;
            this.m[13] = y;
            this.m[14] = z;
            this._markAsUpdated();
            return this;
        }
        /**
         * Inserts the translation vector in the current matrix
         * @param vector3 defines the translation to insert
         * @returns the current updated matrix
         */
        setTranslation(vector3) {
            this.m[12] = vector3.x;
            this.m[13] = vector3.y;
            this.m[14] = vector3.z;
            this._markAsUpdated();
            return this;
        }
        /**
         * Gets the translation value of the current matrix
         * @returns a new Vector3 as the extracted translation from the matrix
         */
        getTranslation() {
            return new Vector3(this.m[12], this.m[13], this.m[14]);
        }
        /**
         * Fill a Vector3 with the extracted translation from the matrix
         * @param result defines the Vector3 where to store the translation
         * @returns the current matrix
         */
        getTranslationToRef(result) {
            result.x = this.m[12];
            result.y = this.m[13];
            result.z = this.m[14];
            return this;
        }
        /**
         * Remove rotation and scaling part from the matrix
         * @returns the updated matrix
         */
        removeRotationAndScaling() {
            this.setRowFromFloats(0, 1, 0, 0, 0);
            this.setRowFromFloats(1, 0, 1, 0, 0);
            this.setRowFromFloats(2, 0, 0, 1, 0);
            return this;
        }
        /**
         * Multiply two matrices
         * @param other defines the second operand
         * @returns a new matrix set with the multiplication result of the current Matrix and the given one
         */
        multiply(other) {
            let result = new Matrix();
            this.multiplyToRef(other, result);
            return result;
        }
        /**
         * Copy the current matrix from the given one
         * @param other defines the source matrix
         * @returns the current updated matrix
         */
        copyFrom(other) {
            for (let index = 0; index < 16; index++) {
                this.m[index] = other.m[index];
            }
            this._markAsUpdated();
            return this;
        }
        /**
         * Populates the given array from the starting index with the current matrix values
         * @param array defines the target array
         * @param offset defines the offset in the target array where to start storing values
         * @returns the current matrix
         */
        copyToArray(array, offset = 0) {
            for (let index = 0; index < 16; index++) {
                array[offset + index] = this.m[index];
            }
            return this;
        }
        /**
         * Sets the given matrix "result" with the multiplication result of the current Matrix and the given one
         * @param other defines the second operand
         * @param result defines the matrix where to store the multiplication
         * @returns the current matrix
         */
        multiplyToRef(other, result) {
            this.multiplyToArray(other, result.m, 0);
            result._markAsUpdated();
            return this;
        }
        /**
         * Sets the Float32Array "result" from the given index "offset" with the multiplication of the current matrix and the given one
         * @param other defines the second operand
         * @param result defines the array where to store the multiplication
         * @param offset defines the offset in the target array where to start storing values
         * @returns the current matrix
         */
        multiplyToArray(other, result, offset) {
            let tm0 = this.m[0];
            let tm1 = this.m[1];
            let tm2 = this.m[2];
            let tm3 = this.m[3];
            let tm4 = this.m[4];
            let tm5 = this.m[5];
            let tm6 = this.m[6];
            let tm7 = this.m[7];
            let tm8 = this.m[8];
            let tm9 = this.m[9];
            let tm10 = this.m[10];
            let tm11 = this.m[11];
            let tm12 = this.m[12];
            let tm13 = this.m[13];
            let tm14 = this.m[14];
            let tm15 = this.m[15];
            let om0 = other.m[0];
            let om1 = other.m[1];
            let om2 = other.m[2];
            let om3 = other.m[3];
            let om4 = other.m[4];
            let om5 = other.m[5];
            let om6 = other.m[6];
            let om7 = other.m[7];
            let om8 = other.m[8];
            let om9 = other.m[9];
            let om10 = other.m[10];
            let om11 = other.m[11];
            let om12 = other.m[12];
            let om13 = other.m[13];
            let om14 = other.m[14];
            let om15 = other.m[15];
            result[offset] = tm0 * om0 + tm1 * om4 + tm2 * om8 + tm3 * om12;
            result[offset + 1] = tm0 * om1 + tm1 * om5 + tm2 * om9 + tm3 * om13;
            result[offset + 2] = tm0 * om2 + tm1 * om6 + tm2 * om10 + tm3 * om14;
            result[offset + 3] = tm0 * om3 + tm1 * om7 + tm2 * om11 + tm3 * om15;
            result[offset + 4] = tm4 * om0 + tm5 * om4 + tm6 * om8 + tm7 * om12;
            result[offset + 5] = tm4 * om1 + tm5 * om5 + tm6 * om9 + tm7 * om13;
            result[offset + 6] = tm4 * om2 + tm5 * om6 + tm6 * om10 + tm7 * om14;
            result[offset + 7] = tm4 * om3 + tm5 * om7 + tm6 * om11 + tm7 * om15;
            result[offset + 8] = tm8 * om0 + tm9 * om4 + tm10 * om8 + tm11 * om12;
            result[offset + 9] = tm8 * om1 + tm9 * om5 + tm10 * om9 + tm11 * om13;
            result[offset + 10] = tm8 * om2 + tm9 * om6 + tm10 * om10 + tm11 * om14;
            result[offset + 11] = tm8 * om3 + tm9 * om7 + tm10 * om11 + tm11 * om15;
            result[offset + 12] = tm12 * om0 + tm13 * om4 + tm14 * om8 + tm15 * om12;
            result[offset + 13] = tm12 * om1 + tm13 * om5 + tm14 * om9 + tm15 * om13;
            result[offset + 14] = tm12 * om2 + tm13 * om6 + tm14 * om10 + tm15 * om14;
            result[offset + 15] = tm12 * om3 + tm13 * om7 + tm14 * om11 + tm15 * om15;
            return this;
        }
        /**
         * Check equality between this matrix and a second one
         * @param value defines the second matrix to compare
         * @returns true is the current matrix and the given one values are strictly equal
         */
        equals(value) {
            return value &&
                (this.m[0] === value.m[0] && this.m[1] === value.m[1] && this.m[2] === value.m[2] && this.m[3] === value.m[3] &&
                    this.m[4] === value.m[4] && this.m[5] === value.m[5] && this.m[6] === value.m[6] && this.m[7] === value.m[7] &&
                    this.m[8] === value.m[8] && this.m[9] === value.m[9] && this.m[10] === value.m[10] && this.m[11] === value.m[11] &&
                    this.m[12] === value.m[12] && this.m[13] === value.m[13] && this.m[14] === value.m[14] && this.m[15] === value.m[15]);
        }
        /**
         * Clone the current matrix
         * @returns a new matrix from the current matrix
         */
        clone() {
            return Matrix.FromValues(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5], this.m[6], this.m[7], this.m[8], this.m[9], this.m[10], this.m[11], this.m[12], this.m[13], this.m[14], this.m[15]);
        }
        /**
         * Returns the name of the current matrix class
         * @returns the string "Matrix"
         */
        getClassName() {
            return "Matrix";
        }
        /**
         * Gets the hash code of the current matrix
         * @returns the hash code
         */
        getHashCode() {
            let hash = this.m[0] || 0;
            for (let i = 1; i < 16; i++) {
                hash = (hash * 397) ^ (this.m[i] || 0);
            }
            return hash;
        }
        /**
         * Decomposes the current Matrix into a translation, rotation and scaling components
         * @param scale defines the scale vector3 given as a reference to update
         * @param rotation defines the rotation quaternion given as a reference to update
         * @param translation defines the translation vector3 given as a reference to update
         * @returns true if operation was successful
         */
        decompose(scale, rotation, translation) {
            if (translation) {
                translation.x = this.m[12];
                translation.y = this.m[13];
                translation.z = this.m[14];
            }
            scale = scale || MathTmp.Vector3[0];
            scale.x = Math.sqrt(this.m[0] * this.m[0] + this.m[1] * this.m[1] + this.m[2] * this.m[2]);
            scale.y = Math.sqrt(this.m[4] * this.m[4] + this.m[5] * this.m[5] + this.m[6] * this.m[6]);
            scale.z = Math.sqrt(this.m[8] * this.m[8] + this.m[9] * this.m[9] + this.m[10] * this.m[10]);
            if (this.determinant() <= 0) {
                scale.y *= -1;
            }
            if (scale.x === 0 || scale.y === 0 || scale.z === 0) {
                if (rotation) {
                    rotation.x = 0;
                    rotation.y = 0;
                    rotation.z = 0;
                    rotation.w = 1;
                }
                return false;
            }
            if (rotation) {
                Matrix.FromValuesToRef(this.m[0] / scale.x, this.m[1] / scale.x, this.m[2] / scale.x, 0, this.m[4] / scale.y, this.m[5] / scale.y, this.m[6] / scale.y, 0, this.m[8] / scale.z, this.m[9] / scale.z, this.m[10] / scale.z, 0, 0, 0, 0, 1, MathTmp.Matrix[0]);
                Quaternion.FromRotationMatrixToRef(MathTmp.Matrix[0], rotation);
            }
            return true;
        }
        /**
         * Gets specific row of the matrix
         * @param index defines the number of the row to get
         * @returns the index-th row of the current matrix as a new Vector4
         */
        getRow(index) {
            if (index < 0 || index > 3) {
                return null;
            }
            let i = index * 4;
            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
        }
        /**
         * Sets the index-th row of the current matrix to the vector4 values
         * @param index defines the number of the row to set
         * @param row defines the target vector4
         * @returns the updated current matrix
         */
        setRow(index, row) {
            if (index < 0 || index > 3) {
                return this;
            }
            let i = index * 4;
            this.m[i + 0] = row.x;
            this.m[i + 1] = row.y;
            this.m[i + 2] = row.z;
            this.m[i + 3] = row.w;
            this._markAsUpdated();
            return this;
        }
        /**
         * Compute the transpose of the matrix
         * @returns the new transposed matrix
         */
        transpose() {
            return Matrix.Transpose(this);
        }
        /**
         * Compute the transpose of the matrix and store it in a given matrix
         * @param result defines the target matrix
         * @returns the current matrix
         */
        transposeToRef(result) {
            Matrix.TransposeToRef(this, result);
            return this;
        }
        /**
         * Sets the index-th row of the current matrix with the given 4 x float values
         * @param index defines the row index
         * @param x defines the x component to set
         * @param y defines the y component to set
         * @param z defines the z component to set
         * @param w defines the w component to set
         * @returns the updated current matrix
         */
        setRowFromFloats(index, x, y, z, w) {
            if (index < 0 || index > 3) {
                return this;
            }
            let i = index * 4;
            this.m[i + 0] = x;
            this.m[i + 1] = y;
            this.m[i + 2] = z;
            this.m[i + 3] = w;
            this._markAsUpdated();
            return this;
        }
        /**
         * Compute a new matrix set with the current matrix values multiplied by scale (float)
         * @param scale defines the scale factor
         * @returns a new matrix
         */
        scale(scale) {
            let result = new Matrix();
            this.scaleToRef(scale, result);
            return result;
        }
        /**
         * Scale the current matrix values by a factor to a given result matrix
         * @param scale defines the scale factor
         * @param result defines the matrix to store the result
         * @returns the current matrix
         */
        scaleToRef(scale, result) {
            for (let index = 0; index < 16; index++) {
                result.m[index] = this.m[index] * scale;
            }
            result._markAsUpdated();
            return this;
        }
        /**
         * Scale the current matrix values by a factor and add the result to a given matrix
         * @param scale defines the scale factor
         * @param result defines the Matrix to store the result
         * @returns the current matrix
         */
        scaleAndAddToRef(scale, result) {
            for (let index = 0; index < 16; index++) {
                result.m[index] += this.m[index] * scale;
            }
            result._markAsUpdated();
            return this;
        }
        /**
         * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).
         * @param ref matrix to store the result
         */
        toNormalMatrix(ref) {
            this.invertToRef(ref);
            ref.transpose();
            let m = ref.m;
            Matrix.FromValuesToRef(m[0], m[1], m[2], 0, m[4], m[5], m[6], 0, m[8], m[9], m[10], 0, 0, 0, 0, 1, ref);
        }
        /**
         * Gets only rotation part of the current matrix
         * @returns a new matrix sets to the extracted rotation matrix from the current one
         */
        getRotationMatrix() {
            let result = Matrix.Identity();
            this.getRotationMatrixToRef(result);
            return result;
        }
        /**
         * Extracts the rotation matrix from the current one and sets it as the given "result"
         * @param result defines the target matrix to store data to
         * @returns the current matrix
         */
        getRotationMatrixToRef(result) {
            let m = this.m;
            let sx = Math.sqrt(m[0] * m[0] + m[1] * m[1] + m[2] * m[2]);
            let sy = Math.sqrt(m[4] * m[4] + m[5] * m[5] + m[6] * m[6]);
            let sz = Math.sqrt(m[8] * m[8] + m[9] * m[9] + m[10] * m[10]);
            if (this.determinant() <= 0) {
                sy *= -1;
            }
            if (sx === 0 || sy === 0 || sz === 0) {
                Matrix.IdentityToRef(result);
            }
            else {
                Matrix.FromValuesToRef(m[0] / sx, m[1] / sx, m[2] / sx, 0, m[4] / sy, m[5] / sy, m[6] / sy, 0, m[8] / sz, m[9] / sz, m[10] / sz, 0, 0, 0, 0, 1, result);
            }
            return this;
        }
        // Statics
        /**
         * Creates a matrix from an array
         * @param array defines the source array
         * @param offset defines an offset in the source array
         * @returns a new Matrix set from the starting index of the given array
         */
        static FromArray(array, offset) {
            let result = new Matrix();
            if (!offset) {
                offset = 0;
            }
            Matrix.FromArrayToRef(array, offset, result);
            return result;
        }
        /**
         * Copy the content of an array into a given matrix
         * @param array defines the source array
         * @param offset defines an offset in the source array
         * @param result defines the target matrix
         */
        static FromArrayToRef(array, offset, result) {
            for (let index = 0; index < 16; index++) {
                result.m[index] = array[index + offset];
            }
            result._markAsUpdated();
        }
        /**
         * Stores an array into a matrix after having multiplied each component by a given factor
         * @param array defines the source array
         * @param offset defines the offset in the source array
         * @param scale defines the scaling factor
         * @param result defines the target matrix
         */
        static FromFloat32ArrayToRefScaled(array, offset, scale, result) {
            for (let index = 0; index < 16; index++) {
                result.m[index] = array[index + offset] * scale;
            }
            result._markAsUpdated();
        }
        /**
         * Stores a list of values (16) inside a given matrix
         * @param initialM11 defines 1st value of 1st row
         * @param initialM12 defines 2nd value of 1st row
         * @param initialM13 defines 3rd value of 1st row
         * @param initialM14 defines 4th value of 1st row
         * @param initialM21 defines 1st value of 2nd row
         * @param initialM22 defines 2nd value of 2nd row
         * @param initialM23 defines 3rd value of 2nd row
         * @param initialM24 defines 4th value of 2nd row
         * @param initialM31 defines 1st value of 3rd row
         * @param initialM32 defines 2nd value of 3rd row
         * @param initialM33 defines 3rd value of 3rd row
         * @param initialM34 defines 4th value of 3rd row
         * @param initialM41 defines 1st value of 4th row
         * @param initialM42 defines 2nd value of 4th row
         * @param initialM43 defines 3rd value of 4th row
         * @param initialM44 defines 4th value of 4th row
         * @param result defines the target matrix
         */
        static FromValuesToRef(initialM11, initialM12, initialM13, initialM14, initialM21, initialM22, initialM23, initialM24, initialM31, initialM32, initialM33, initialM34, initialM41, initialM42, initialM43, initialM44, result) {
            result.m[0] = initialM11;
            result.m[1] = initialM12;
            result.m[2] = initialM13;
            result.m[3] = initialM14;
            result.m[4] = initialM21;
            result.m[5] = initialM22;
            result.m[6] = initialM23;
            result.m[7] = initialM24;
            result.m[8] = initialM31;
            result.m[9] = initialM32;
            result.m[10] = initialM33;
            result.m[11] = initialM34;
            result.m[12] = initialM41;
            result.m[13] = initialM42;
            result.m[14] = initialM43;
            result.m[15] = initialM44;
            result._markAsUpdated();
        }
        /**
         * Gets an identity matrix that must not be updated
         */
        static get IdentityReadOnly() {
            return Matrix._identityReadOnly;
        }
        /**
         * Creates new matrix from a list of values (16)
         * @param initialM11 defines 1st value of 1st row
         * @param initialM12 defines 2nd value of 1st row
         * @param initialM13 defines 3rd value of 1st row
         * @param initialM14 defines 4th value of 1st row
         * @param initialM21 defines 1st value of 2nd row
         * @param initialM22 defines 2nd value of 2nd row
         * @param initialM23 defines 3rd value of 2nd row
         * @param initialM24 defines 4th value of 2nd row
         * @param initialM31 defines 1st value of 3rd row
         * @param initialM32 defines 2nd value of 3rd row
         * @param initialM33 defines 3rd value of 3rd row
         * @param initialM34 defines 4th value of 3rd row
         * @param initialM41 defines 1st value of 4th row
         * @param initialM42 defines 2nd value of 4th row
         * @param initialM43 defines 3rd value of 4th row
         * @param initialM44 defines 4th value of 4th row
         * @returns the new matrix
         */
        static FromValues(initialM11, initialM12, initialM13, initialM14, initialM21, initialM22, initialM23, initialM24, initialM31, initialM32, initialM33, initialM34, initialM41, initialM42, initialM43, initialM44) {
            let result = new Matrix();
            result.m[0] = initialM11;
            result.m[1] = initialM12;
            result.m[2] = initialM13;
            result.m[3] = initialM14;
            result.m[4] = initialM21;
            result.m[5] = initialM22;
            result.m[6] = initialM23;
            result.m[7] = initialM24;
            result.m[8] = initialM31;
            result.m[9] = initialM32;
            result.m[10] = initialM33;
            result.m[11] = initialM34;
            result.m[12] = initialM41;
            result.m[13] = initialM42;
            result.m[14] = initialM43;
            result.m[15] = initialM44;
            return result;
        }
        /**
         * Creates a new matrix composed by merging scale (vector3), rotation (quaternion) and translation (vector3)
         * @param scale defines the scale vector3
         * @param rotation defines the rotation quaternion
         * @param translation defines the translation vector3
         * @returns a new matrix
         */
        static Compose(scale, rotation, translation) {
            let result = Matrix.Identity();
            Matrix.ComposeToRef(scale, rotation, translation, result);
            return result;
        }
        /**
         * Sets a matrix to a value composed by merging scale (vector3), rotation (quaternion) and translation (vector3)
         * @param scale defines the scale vector3
         * @param rotation defines the rotation quaternion
         * @param translation defines the translation vector3
         * @param result defines the target matrix
         */
        static ComposeToRef(scale, rotation, translation, result) {
            Matrix.FromValuesToRef(scale.x, 0, 0, 0, 0, scale.y, 0, 0, 0, 0, scale.z, 0, 0, 0, 0, 1, MathTmp.Matrix[1]);
            rotation.toRotationMatrix(MathTmp.Matrix[0]);
            MathTmp.Matrix[1].multiplyToRef(MathTmp.Matrix[0], result);
            result.setTranslation(translation);
        }
        /**
         * Creates a new identity matrix
         * @returns a new identity matrix
         */
        static Identity() {
            return Matrix.FromValues(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);
        }
        /**
         * Creates a new identity matrix and stores the result in a given matrix
         * @param result defines the target matrix
         */
        static IdentityToRef(result) {
            Matrix.FromValuesToRef(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, result);
        }
        /**
         * Creates a new zero matrix
         * @returns a new zero matrix
         */
        static Zero() {
            return Matrix.FromValues(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the X axis
         * @param angle defines the angle (in radians) to use
         * @return the new matrix
         */
        static RotationX(angle) {
            let result = new Matrix();
            Matrix.RotationXToRef(angle, result);
            return result;
        }
        /**
         * Creates a new matrix as the invert of a given matrix
         * @param source defines the source matrix
         * @returns the new matrix
         */
        static Invert(source) {
            let result = new Matrix();
            source.invertToRef(result);
            return result;
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the X axis and stores it in a given matrix
         * @param angle defines the angle (in radians) to use
         * @param result defines the target matrix
         */
        static RotationXToRef(angle, result) {
            let s = Math.sin(angle);
            let c = Math.cos(angle);
            result.m[0] = 1.0;
            result.m[15] = 1.0;
            result.m[5] = c;
            result.m[10] = c;
            result.m[9] = -s;
            result.m[6] = s;
            result.m[1] = 0.0;
            result.m[2] = 0.0;
            result.m[3] = 0.0;
            result.m[4] = 0.0;
            result.m[7] = 0.0;
            result.m[8] = 0.0;
            result.m[11] = 0.0;
            result.m[12] = 0.0;
            result.m[13] = 0.0;
            result.m[14] = 0.0;
            result._markAsUpdated();
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the Y axis
         * @param angle defines the angle (in radians) to use
         * @return the new matrix
         */
        static RotationY(angle) {
            let result = new Matrix();
            Matrix.RotationYToRef(angle, result);
            return result;
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the Y axis and stores it in a given matrix
         * @param angle defines the angle (in radians) to use
         * @param result defines the target matrix
         */
        static RotationYToRef(angle, result) {
            let s = Math.sin(angle);
            let c = Math.cos(angle);
            result.m[5] = 1.0;
            result.m[15] = 1.0;
            result.m[0] = c;
            result.m[2] = -s;
            result.m[8] = s;
            result.m[10] = c;
            result.m[1] = 0.0;
            result.m[3] = 0.0;
            result.m[4] = 0.0;
            result.m[6] = 0.0;
            result.m[7] = 0.0;
            result.m[9] = 0.0;
            result.m[11] = 0.0;
            result.m[12] = 0.0;
            result.m[13] = 0.0;
            result.m[14] = 0.0;
            result._markAsUpdated();
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the Z axis
         * @param angle defines the angle (in radians) to use
         * @return the new matrix
         */
        static RotationZ(angle) {
            let result = new Matrix();
            Matrix.RotationZToRef(angle, result);
            return result;
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the Z axis and stores it in a given matrix
         * @param angle defines the angle (in radians) to use
         * @param result defines the target matrix
         */
        static RotationZToRef(angle, result) {
            let s = Math.sin(angle);
            let c = Math.cos(angle);
            result.m[10] = 1.0;
            result.m[15] = 1.0;
            result.m[0] = c;
            result.m[1] = s;
            result.m[4] = -s;
            result.m[5] = c;
            result.m[2] = 0.0;
            result.m[3] = 0.0;
            result.m[6] = 0.0;
            result.m[7] = 0.0;
            result.m[8] = 0.0;
            result.m[9] = 0.0;
            result.m[11] = 0.0;
            result.m[12] = 0.0;
            result.m[13] = 0.0;
            result.m[14] = 0.0;
            result._markAsUpdated();
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the given axis
         * @param axis defines the axis to use
         * @param angle defines the angle (in radians) to use
         * @return the new matrix
         */
        static RotationAxis(axis, angle) {
            let result = Matrix.Zero();
            Matrix.RotationAxisToRef(axis, angle, result);
            return result;
        }
        /**
         * Creates a new rotation matrix for "angle" radians around the given axis and stores it in a given matrix
         * @param axis defines the axis to use
         * @param angle defines the angle (in radians) to use
         * @param result defines the target matrix
         */
        static RotationAxisToRef(axis, angle, result) {
            let s = Math.sin(-angle);
            let c = Math.cos(-angle);
            let c1 = 1 - c;
            axis.normalize();
            result.m[0] = (axis.x * axis.x) * c1 + c;
            result.m[1] = (axis.x * axis.y) * c1 - (axis.z * s);
            result.m[2] = (axis.x * axis.z) * c1 + (axis.y * s);
            result.m[3] = 0.0;
            result.m[4] = (axis.y * axis.x) * c1 + (axis.z * s);
            result.m[5] = (axis.y * axis.y) * c1 + c;
            result.m[6] = (axis.y * axis.z) * c1 - (axis.x * s);
            result.m[7] = 0.0;
            result.m[8] = (axis.z * axis.x) * c1 - (axis.y * s);
            result.m[9] = (axis.z * axis.y) * c1 + (axis.x * s);
            result.m[10] = (axis.z * axis.z) * c1 + c;
            result.m[11] = 0.0;
            result.m[15] = 1.0;
            result._markAsUpdated();
        }
        /**
         * Creates a rotation matrix
         * @param yaw defines the yaw angle in radians (Y axis)
         * @param pitch defines the pitch angle in radians (X axis)
         * @param roll defines the roll angle in radians (X axis)
         * @returns the new rotation matrix
         */
        static RotationYawPitchRoll(yaw, pitch, roll) {
            let result = new Matrix();
            Matrix.RotationYawPitchRollToRef(yaw, pitch, roll, result);
            return result;
        }
        /**
         * Creates a rotation matrix and stores it in a given matrix
         * @param yaw defines the yaw angle in radians (Y axis)
         * @param pitch defines the pitch angle in radians (X axis)
         * @param roll defines the roll angle in radians (X axis)
         * @param result defines the target matrix
         */
        static RotationYawPitchRollToRef(yaw, pitch, roll, result) {
            Quaternion.RotationYawPitchRollToRef(yaw, pitch, roll, this._tempQuaternion);
            this._tempQuaternion.toRotationMatrix(result);
        }
        /**
         * Creates a scaling matrix
         * @param x defines the scale factor on X axis
         * @param y defines the scale factor on Y axis
         * @param z defines the scale factor on Z axis
         * @returns the new matrix
         */
        static Scaling(x, y, z) {
            let result = Matrix.Zero();
            Matrix.ScalingToRef(x, y, z, result);
            return result;
        }
        /**
         * Creates a scaling matrix and stores it in a given matrix
         * @param x defines the scale factor on X axis
         * @param y defines the scale factor on Y axis
         * @param z defines the scale factor on Z axis
         * @param result defines the target matrix
         */
        static ScalingToRef(x, y, z, result) {
            result.m[0] = x;
            result.m[1] = 0.0;
            result.m[2] = 0.0;
            result.m[3] = 0.0;
            result.m[4] = 0.0;
            result.m[5] = y;
            result.m[6] = 0.0;
            result.m[7] = 0.0;
            result.m[8] = 0.0;
            result.m[9] = 0.0;
            result.m[10] = z;
            result.m[11] = 0.0;
            result.m[12] = 0.0;
            result.m[13] = 0.0;
            result.m[14] = 0.0;
            result.m[15] = 1.0;
            result._markAsUpdated();
        }
        /**
         * Creates a translation matrix
         * @param x defines the translation on X axis
         * @param y defines the translation on Y axis
         * @param z defines the translationon Z axis
         * @returns the new matrix
         */
        static Translation(x, y, z) {
            let result = Matrix.Identity();
            Matrix.TranslationToRef(x, y, z, result);
            return result;
        }
        /**
         * Creates a translation matrix and stores it in a given matrix
         * @param x defines the translation on X axis
         * @param y defines the translation on Y axis
         * @param z defines the translationon Z axis
         * @param result defines the target matrix
         */
        static TranslationToRef(x, y, z, result) {
            Matrix.FromValuesToRef(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, x, y, z, 1.0, result);
        }
        /**
         * Returns a new Matrix whose values are the interpolated values for "gradient" (float) between the ones of the matrices "startValue" and "endValue".
         * @param startValue defines the start value
         * @param endValue defines the end value
         * @param gradient defines the gradient factor
         * @returns the new matrix
         */
        static Lerp(startValue, endValue, gradient) {
            let result = Matrix.Zero();
            Matrix.LerpToRef(startValue, endValue, gradient, result);
            return result;
        }
        /**
         * Set the given matrix "result" as the interpolated values for "gradient" (float) between the ones of the matrices "startValue" and "endValue".
         * @param startValue defines the start value
         * @param endValue defines the end value
         * @param gradient defines the gradient factor
         * @param result defines the Matrix object where to store data
         */
        static LerpToRef(startValue, endValue, gradient, result) {
            for (let index = 0; index < 16; index++) {
                result.m[index] = startValue.m[index] * (1.0 - gradient) + endValue.m[index] * gradient;
            }
            result._markAsUpdated();
        }
        /**
         * Builds a new matrix whose values are computed by:
         * * decomposing the the "startValue" and "endValue" matrices into their respective scale, rotation and translation matrices
         * * interpolating for "gradient" (float) the values between each of these decomposed matrices between the start and the end
         * * recomposing a new matrix from these 3 interpolated scale, rotation and translation matrices
         * @param startValue defines the first matrix
         * @param endValue defines the second matrix
         * @param gradient defines the gradient between the two matrices
         * @returns the new matrix
         */
        static DecomposeLerp(startValue, endValue, gradient) {
            let result = Matrix.Zero();
            Matrix.DecomposeLerpToRef(startValue, endValue, gradient, result);
            return result;
        }
        /**
         * Update a matrix to values which are computed by:
         * * decomposing the the "startValue" and "endValue" matrices into their respective scale, rotation and translation matrices
         * * interpolating for "gradient" (float) the values between each of these decomposed matrices between the start and the end
         * * recomposing a new matrix from these 3 interpolated scale, rotation and translation matrices
         * @param startValue defines the first matrix
         * @param endValue defines the second matrix
         * @param gradient defines the gradient between the two matrices
         * @param result defines the target matrix
         */
        static DecomposeLerpToRef(startValue, endValue, gradient, result) {
            let startScale = MathTmp.Vector3[0];
            let startRotation = MathTmp.Quaternion[0];
            let startTranslation = MathTmp.Vector3[1];
            startValue.decompose(startScale, startRotation, startTranslation);
            let endScale = MathTmp.Vector3[2];
            let endRotation = MathTmp.Quaternion[1];
            let endTranslation = MathTmp.Vector3[3];
            endValue.decompose(endScale, endRotation, endTranslation);
            let resultScale = MathTmp.Vector3[4];
            Vector3.LerpToRef(startScale, endScale, gradient, resultScale);
            let resultRotation = MathTmp.Quaternion[2];
            Quaternion.SlerpToRef(startRotation, endRotation, gradient, resultRotation);
            let resultTranslation = MathTmp.Vector3[5];
            Vector3.LerpToRef(startTranslation, endTranslation, gradient, resultTranslation);
            Matrix.ComposeToRef(resultScale, resultRotation, resultTranslation, result);
        }
        /**
         * Gets a new rotation matrix used to rotate an entity so as it looks at the target vector3, from the eye vector3 position, the up vector3 being oriented like "up"
         * This function works in left handed mode
         * @param eye defines the final position of the entity
         * @param target defines where the entity should look at
         * @param up defines the up vector for the entity
         * @returns the new matrix
         */
        static LookAtLH(eye, target, up) {
            let result = Matrix.Zero();
            Matrix.LookAtLHToRef(eye, target, up, result);
            return result;
        }
        /**
         * Sets the given "result" Matrix to a rotation matrix used to rotate an
         * entity so that it looks at the target vector3, from the eye vector3 position, the up vector3 being oriented like "up".
         * This function works in left handed mode
         * @param eye defines the final position of the entity
         * @param target defines where the entity should look at
         * @param up defines the up vector for the entity
         * @param result defines the target matrix
         */
        static LookAtLHToRef(eye, target, up, result) {
            // Z axis
            target.subtractToRef(eye, this._zAxis);
            this._zAxis.normalize();
            // X axis
            Vector3.CrossToRef(up, this._zAxis, this._xAxis);
            if (this._xAxis.lengthSquared() === 0) {
                this._xAxis.x = 1.0;
            }
            else {
                this._xAxis.normalize();
            }
            // Y axis
            Vector3.CrossToRef(this._zAxis, this._xAxis, this._yAxis);
            this._yAxis.normalize();
            // Eye angles
            let ex = -Vector3.Dot(this._xAxis, eye);
            let ey = -Vector3.Dot(this._yAxis, eye);
            let ez = -Vector3.Dot(this._zAxis, eye);
            return Matrix.FromValuesToRef(this._xAxis.x, this._yAxis.x, this._zAxis.x, 0, this._xAxis.y, this._yAxis.y, this._zAxis.y, 0, this._xAxis.z, this._yAxis.z, this._zAxis.z, 0, ex, ey, ez, 1, result);
        }
        /**
         * Gets a new rotation matrix used to rotate an entity so as it looks at the target vector3, from the eye vector3 position, the up vector3 being oriented like "up"
         * This function works in right handed mode
         * @param eye defines the final position of the entity
         * @param target defines where the entity should look at
         * @param up defines the up vector for the entity
         * @returns the new matrix
         */
        static LookAtRH(eye, target, up) {
            let result = Matrix.Zero();
            Matrix.LookAtRHToRef(eye, target, up, result);
            return result;
        }
        /**
         * Sets the given "result" Matrix to a rotation matrix used to rotate an entity so
         * that it looks at the target vector3, from the eye vector3 position, the up vector3 being oriented like "up".
         * This function works in right handed mode
         * @param eye defines the final position of the entity
         * @param target defines where the entity should look at
         * @param up defines the up vector for the entity
         * @param result defines the target matrix
         */
        static LookAtRHToRef(eye, target, up, result) {
            // Z axis
            eye.subtractToRef(target, this._zAxis);
            this._zAxis.normalize();
            // X axis
            Vector3.CrossToRef(up, this._zAxis, this._xAxis);
            if (this._xAxis.lengthSquared() === 0) {
                this._xAxis.x = 1.0;
            }
            else {
                this._xAxis.normalize();
            }
            // Y axis
            Vector3.CrossToRef(this._zAxis, this._xAxis, this._yAxis);
            this._yAxis.normalize();
            // Eye angles
            let ex = -Vector3.Dot(this._xAxis, eye);
            let ey = -Vector3.Dot(this._yAxis, eye);
            let ez = -Vector3.Dot(this._zAxis, eye);
            return Matrix.FromValuesToRef(this._xAxis.x, this._yAxis.x, this._zAxis.x, 0, this._xAxis.y, this._yAxis.y, this._zAxis.y, 0, this._xAxis.z, this._yAxis.z, this._zAxis.z, 0, ex, ey, ez, 1, result);
        }
        /**
         * Create a left-handed orthographic projection matrix
         * @param width defines the viewport width
         * @param height defines the viewport height
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @returns a new matrix as a left-handed orthographic projection matrix
         */
        static OrthoLH(width, height, znear, zfar) {
            let matrix = Matrix.Zero();
            Matrix.OrthoLHToRef(width, height, znear, zfar, matrix);
            return matrix;
        }
        /**
         * Store a left-handed orthographic projection to a given matrix
         * @param width defines the viewport width
         * @param height defines the viewport height
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @param result defines the target matrix
         */
        static OrthoLHToRef(width, height, znear, zfar, result) {
            let n = znear;
            let f = zfar;
            let a = 2.0 / width;
            let b = 2.0 / height;
            let c = 2.0 / (f - n);
            let d = -(f + n) / (f - n);
            Matrix.FromValuesToRef(a, 0.0, 0.0, 0.0, 0.0, b, 0.0, 0.0, 0.0, 0.0, c, 0.0, 0.0, 0.0, d, 1.0, result);
        }
        /**
         * Create a left-handed orthographic projection matrix
         * @param left defines the viewport left coordinate
         * @param right defines the viewport right coordinate
         * @param bottom defines the viewport bottom coordinate
         * @param top defines the viewport top coordinate
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @returns a new matrix as a left-handed orthographic projection matrix
         */
        static OrthoOffCenterLH(left, right, bottom, top, znear, zfar) {
            let matrix = Matrix.Zero();
            Matrix.OrthoOffCenterLHToRef(left, right, bottom, top, znear, zfar, matrix);
            return matrix;
        }
        /**
         * Stores a left-handed orthographic projection into a given matrix
         * @param left defines the viewport left coordinate
         * @param right defines the viewport right coordinate
         * @param bottom defines the viewport bottom coordinate
         * @param top defines the viewport top coordinate
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @param result defines the target matrix
         */
        static OrthoOffCenterLHToRef(left, right, bottom, top, znear, zfar, result) {
            let n = znear;
            let f = zfar;
            let a = 2.0 / (right - left);
            let b = 2.0 / (top - bottom);
            let c = 2.0 / (f - n);
            let d = -(f + n) / (f - n);
            let i0 = (left + right) / (left - right);
            let i1 = (top + bottom) / (bottom - top);
            Matrix.FromValuesToRef(a, 0.0, 0.0, 0.0, 0.0, b, 0.0, 0.0, 0.0, 0.0, c, 0.0, i0, i1, d, 1.0, result);
        }
        /**
         * Creates a right-handed orthographic projection matrix
         * @param left defines the viewport left coordinate
         * @param right defines the viewport right coordinate
         * @param bottom defines the viewport bottom coordinate
         * @param top defines the viewport top coordinate
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @returns a new matrix as a right-handed orthographic projection matrix
         */
        static OrthoOffCenterRH(left, right, bottom, top, znear, zfar) {
            let matrix = Matrix.Zero();
            Matrix.OrthoOffCenterRHToRef(left, right, bottom, top, znear, zfar, matrix);
            return matrix;
        }
        /**
         * Stores a right-handed orthographic projection into a given matrix
         * @param left defines the viewport left coordinate
         * @param right defines the viewport right coordinate
         * @param bottom defines the viewport bottom coordinate
         * @param top defines the viewport top coordinate
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @param result defines the target matrix
         */
        static OrthoOffCenterRHToRef(left, right, bottom, top, znear, zfar, result) {
            Matrix.OrthoOffCenterLHToRef(left, right, bottom, top, znear, zfar, result);
            result.m[10] *= -1.0;
        }
        /**
         * Creates a left-handed perspective projection matrix
         * @param width defines the viewport width
         * @param height defines the viewport height
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @returns a new matrix as a left-handed perspective projection matrix
         */
        static PerspectiveLH(width, height, znear, zfar) {
            let matrix = Matrix.Zero();
            let n = znear;
            let f = zfar;
            let a = 2.0 * n / width;
            let b = 2.0 * n / height;
            let c = (f + n) / (f - n);
            let d = -2.0 * f * n / (f - n);
            Matrix.FromValuesToRef(a, 0.0, 0.0, 0.0, 0.0, b, 0.0, 0.0, 0.0, 0.0, c, 1.0, 0.0, 0.0, d, 0.0, matrix);
            return matrix;
        }
        /**
         * Creates a left-handed perspective projection matrix
         * @param fov defines the horizontal field of view
         * @param aspect defines the aspect ratio
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @returns a new matrix as a left-handed perspective projection matrix
         */
        static PerspectiveFovLH(fov, aspect, znear, zfar) {
            let matrix = Matrix.Zero();
            Matrix.PerspectiveFovLHToRef(fov, aspect, znear, zfar, matrix);
            return matrix;
        }
        /**
         * Stores a left-handed perspective projection into a given matrix
         * @param fov defines the horizontal field of view
         * @param aspect defines the aspect ratio
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @param result defines the target matrix
         * @param isVerticalFovFixed defines it the fov is vertically fixed (default) or horizontally
         */
        static PerspectiveFovLHToRef(fov, aspect, znear, zfar, result, isVerticalFovFixed = true) {
            let n = znear;
            let f = zfar;
            let t = 1.0 / (Math.tan(fov * 0.5));
            let a = isVerticalFovFixed ? (t / aspect) : t;
            let b = isVerticalFovFixed ? t : (t * aspect);
            let c = (f + n) / (f - n);
            let d = -2.0 * f * n / (f - n);
            Matrix.FromValuesToRef(a, 0.0, 0.0, 0.0, 0.0, b, 0.0, 0.0, 0.0, 0.0, c, 1.0, 0.0, 0.0, d, 0.0, result);
        }
        /**
         * Creates a right-handed perspective projection matrix
         * @param fov defines the horizontal field of view
         * @param aspect defines the aspect ratio
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @returns a new matrix as a right-handed perspective projection matrix
         */
        static PerspectiveFovRH(fov, aspect, znear, zfar) {
            let matrix = Matrix.Zero();
            Matrix.PerspectiveFovRHToRef(fov, aspect, znear, zfar, matrix);
            return matrix;
        }
        /**
         * Stores a right-handed perspective projection into a given matrix
         * @param fov defines the horizontal field of view
         * @param aspect defines the aspect ratio
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @param result defines the target matrix
         * @param isVerticalFovFixed defines it the fov is vertically fixed (default) or horizontally
         */
        static PerspectiveFovRHToRef(fov, aspect, znear, zfar, result, isVerticalFovFixed = true) {
            //alternatively this could be expressed as:
            //    m = PerspectiveFovLHToRef
            //    m[10] *= -1.0;
            //    m[11] *= -1.0;
            let n = znear;
            let f = zfar;
            let t = 1.0 / (Math.tan(fov * 0.5));
            let a = isVerticalFovFixed ? (t / aspect) : t;
            let b = isVerticalFovFixed ? t : (t * aspect);
            let c = -(f + n) / (f - n);
            let d = -2 * f * n / (f - n);
            Matrix.FromValuesToRef(a, 0.0, 0.0, 0.0, 0.0, b, 0.0, 0.0, 0.0, 0.0, c, -1.0, 0.0, 0.0, d, 0.0, result);
        }
        /**
         * Stores a perspective projection for WebVR info a given matrix
         * @param fov defines the field of view
         * @param znear defines the near clip plane
         * @param zfar defines the far clip plane
         * @param result defines the target matrix
         * @param rightHanded defines if the matrix must be in right-handed mode (false by default)
         */
        static PerspectiveFovWebVRToRef(fov, znear, zfar, result, rightHanded = false) {
            let rightHandedFactor = rightHanded ? -1 : 1;
            let upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
            let downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
            let leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
            let rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
            let xScale = 2.0 / (leftTan + rightTan);
            let yScale = 2.0 / (upTan + downTan);
            result.m[0] = xScale;
            result.m[1] = result.m[2] = result.m[3] = result.m[4] = 0.0;
            result.m[5] = yScale;
            result.m[6] = result.m[7] = 0.0;
            result.m[8] = ((leftTan - rightTan) * xScale * 0.5);
            result.m[9] = -((upTan - downTan) * yScale * 0.5);
            result.m[10] = -zfar / (znear - zfar);
            result.m[11] = 1.0 * rightHandedFactor;
            result.m[12] = result.m[13] = result.m[15] = 0.0;
            result.m[14] = -(2.0 * zfar * znear) / (zfar - znear);
            result._markAsUpdated();
        }
        /**
         * Computes a complete transformation matrix
         * @param viewport defines the viewport to use
         * @param world defines the world matrix
         * @param view defines the view matrix
         * @param projection defines the projection matrix
         * @param zmin defines the near clip plane
         * @param zmax defines the far clip plane
         * @returns the transformation matrix
         */
        static GetFinalMatrix(viewport, world, view, projection, zmin, zmax) {
            let cw = viewport.w;
            let ch = viewport.h;
            let cx = viewport.x;
            let cy = viewport.y;
            let viewportMatrix = Matrix.FromValues(cw / 2.0, 0.0, 0.0, 0.0, 0.0, -ch / 2.0, 0.0, 0.0, 0.0, 0.0, zmax - zmin, 0.0, cx + cw / 2.0, ch / 2.0 + cy, zmin, 1);
            return world.multiply(view)
                .multiply(projection)
                .multiply(viewportMatrix);
        }
        /**
         * Extracts a 2x2 matrix from a given matrix and store the result in a Float32Array
         * @param matrix defines the matrix to use
         * @returns a new Float32Array array with 4 elements : the 2x2 matrix extracted from the given matrix
         */
        static GetAsMatrix2x2(matrix) {
            return new Float32Array([
                matrix.m[0], matrix.m[1],
                matrix.m[4], matrix.m[5]
            ]);
        }
        /**
         * Extracts a 3x3 matrix from a given matrix and store the result in a Float32Array
         * @param matrix defines the matrix to use
         * @returns a new Float32Array array with 9 elements : the 3x3 matrix extracted from the given matrix
         */
        static GetAsMatrix3x3(matrix) {
            return new Float32Array([
                matrix.m[0], matrix.m[1], matrix.m[2],
                matrix.m[4], matrix.m[5], matrix.m[6],
                matrix.m[8], matrix.m[9], matrix.m[10]
            ]);
        }
        /**
         * Compute the transpose of a given matrix
         * @param matrix defines the matrix to transpose
         * @returns the new matrix
         */
        static Transpose(matrix) {
            let result = new Matrix();
            Matrix.TransposeToRef(matrix, result);
            return result;
        }
        /**
         * Compute the transpose of a matrix and store it in a target matrix
         * @param matrix defines the matrix to transpose
         * @param result defines the target matrix
         */
        static TransposeToRef(matrix, result) {
            result.m[0] = matrix.m[0];
            result.m[1] = matrix.m[4];
            result.m[2] = matrix.m[8];
            result.m[3] = matrix.m[12];
            result.m[4] = matrix.m[1];
            result.m[5] = matrix.m[5];
            result.m[6] = matrix.m[9];
            result.m[7] = matrix.m[13];
            result.m[8] = matrix.m[2];
            result.m[9] = matrix.m[6];
            result.m[10] = matrix.m[10];
            result.m[11] = matrix.m[14];
            result.m[12] = matrix.m[3];
            result.m[13] = matrix.m[7];
            result.m[14] = matrix.m[11];
            result.m[15] = matrix.m[15];
        }
        /**
         * Computes a reflection matrix from a plane
         * @param plane defines the reflection plane
         * @returns a new matrix
         */
        static Reflection(plane) {
            let matrix = new Matrix();
            Matrix.ReflectionToRef(plane, matrix);
            return matrix;
        }
        /**
         * Computes a reflection matrix from a plane
         * @param plane defines the reflection plane
         * @param result defines the target matrix
         */
        static ReflectionToRef(plane, result) {
            plane.normalize();
            let x = plane.normal.x;
            let y = plane.normal.y;
            let z = plane.normal.z;
            let temp = -2 * x;
            let temp2 = -2 * y;
            let temp3 = -2 * z;
            result.m[0] = (temp * x) + 1;
            result.m[1] = temp2 * x;
            result.m[2] = temp3 * x;
            result.m[3] = 0.0;
            result.m[4] = temp * y;
            result.m[5] = (temp2 * y) + 1;
            result.m[6] = temp3 * y;
            result.m[7] = 0.0;
            result.m[8] = temp * z;
            result.m[9] = temp2 * z;
            result.m[10] = (temp3 * z) + 1;
            result.m[11] = 0.0;
            result.m[12] = temp * plane.d;
            result.m[13] = temp2 * plane.d;
            result.m[14] = temp3 * plane.d;
            result.m[15] = 1.0;
            result._markAsUpdated();
        }
        /**
         * Sets the given matrix as a rotation matrix composed from the 3 left handed axes
         * @param xaxis defines the value of the 1st axis
         * @param yaxis defines the value of the 2nd axis
         * @param zaxis defines the value of the 3rd axis
         * @param result defines the target matrix
         */
        static FromXYZAxesToRef(xaxis, yaxis, zaxis, result) {
            result.m[0] = xaxis.x;
            result.m[1] = xaxis.y;
            result.m[2] = xaxis.z;
            result.m[3] = 0.0;
            result.m[4] = yaxis.x;
            result.m[5] = yaxis.y;
            result.m[6] = yaxis.z;
            result.m[7] = 0.0;
            result.m[8] = zaxis.x;
            result.m[9] = zaxis.y;
            result.m[10] = zaxis.z;
            result.m[11] = 0.0;
            result.m[12] = 0.0;
            result.m[13] = 0.0;
            result.m[14] = 0.0;
            result.m[15] = 1.0;
            result._markAsUpdated();
        }
        /**
         * Creates a rotation matrix from a quaternion and stores it in a target matrix
         * @param quat defines the quaternion to use
         * @param result defines the target matrix
         */
        static FromQuaternionToRef(quat, result) {
            let xx = quat.x * quat.x;
            let yy = quat.y * quat.y;
            let zz = quat.z * quat.z;
            let xy = quat.x * quat.y;
            let zw = quat.z * quat.w;
            let zx = quat.z * quat.x;
            let yw = quat.y * quat.w;
            let yz = quat.y * quat.z;
            let xw = quat.x * quat.w;
            result.m[0] = 1.0 - (2.0 * (yy + zz));
            result.m[1] = 2.0 * (xy + zw);
            result.m[2] = 2.0 * (zx - yw);
            result.m[3] = 0.0;
            result.m[4] = 2.0 * (xy - zw);
            result.m[5] = 1.0 - (2.0 * (zz + xx));
            result.m[6] = 2.0 * (yz + xw);
            result.m[7] = 0.0;
            result.m[8] = 2.0 * (zx + yw);
            result.m[9] = 2.0 * (yz - xw);
            result.m[10] = 1.0 - (2.0 * (yy + xx));
            result.m[11] = 0.0;
            result.m[12] = 0.0;
            result.m[13] = 0.0;
            result.m[14] = 0.0;
            result.m[15] = 1.0;
            result._markAsUpdated();
        }
    }
    Matrix._tempQuaternion = new Quaternion();
    Matrix._xAxis = Vector3.Zero();
    Matrix._yAxis = Vector3.Zero();
    Matrix._zAxis = Vector3.Zero();
    Matrix._updateFlagSeed = 0;
    Matrix._identityReadOnly = Matrix.Identity();
    FIREFLYX.Matrix = Matrix;
    class Plane {
        /**
         * Creates a Plane object according to the given floats a, b, c, d and the plane equation : ax + by + cz + d = 0
         */
        constructor(a, b, c, d) {
            this.normal = new Vector3(a, b, c);
            this.d = d;
        }
        /**
         * Returns the plane coordinates as a new array of 4 elements [a, b, c, d].
         */
        asArray() {
            return [this.normal.x, this.normal.y, this.normal.z, this.d];
        }
        // Methods
        /**
         * Returns a new plane copied from the current Plane.
         */
        clone() {
            return new Plane(this.normal.x, this.normal.y, this.normal.z, this.d);
        }
        /**
         * Returns the string "Plane".
         */
        getClassName() {
            return "Plane";
        }
        /**
         * Returns the Plane hash code.
         */
        getHashCode() {
            let hash = this.normal.getHashCode();
            hash = (hash * 397) ^ (this.d || 0);
            return hash;
        }
        /**
         * Normalize the current Plane in place.
         * Returns the updated Plane.
         */
        normalize() {
            let norm = (Math.sqrt((this.normal.x * this.normal.x) + (this.normal.y * this.normal.y) + (this.normal.z * this.normal.z)));
            let magnitude = 0.0;
            if (norm !== 0) {
                magnitude = 1.0 / norm;
            }
            this.normal.x *= magnitude;
            this.normal.y *= magnitude;
            this.normal.z *= magnitude;
            this.d *= magnitude;
            return this;
        }
        /**
         * Returns a new Plane as the result of the transformation of the current Plane by the given matrix.
         */
        transform(transformation) {
            let transposedMatrix = Matrix.Transpose(transformation);
            let x = this.normal.x;
            let y = this.normal.y;
            let z = this.normal.z;
            let d = this.d;
            let normalX = (((x * transposedMatrix.m[0]) + (y * transposedMatrix.m[1])) + (z * transposedMatrix.m[2])) + (d * transposedMatrix.m[3]);
            let normalY = (((x * transposedMatrix.m[4]) + (y * transposedMatrix.m[5])) + (z * transposedMatrix.m[6])) + (d * transposedMatrix.m[7]);
            let normalZ = (((x * transposedMatrix.m[8]) + (y * transposedMatrix.m[9])) + (z * transposedMatrix.m[10])) + (d * transposedMatrix.m[11]);
            let finalD = (((x * transposedMatrix.m[12]) + (y * transposedMatrix.m[13])) + (z * transposedMatrix.m[14])) + (d * transposedMatrix.m[15]);
            return new Plane(normalX, normalY, normalZ, finalD);
        }
        /**
         * Returns the dot product (float) of the point coordinates and the plane normal.
         */
        dotCoordinate(point) {
            return ((((this.normal.x * point.x) + (this.normal.y * point.y)) + (this.normal.z * point.z)) + this.d);
        }
        /**
         * Updates the current Plane from the plane defined by the three given points.
         * Returns the updated Plane.
         */
        copyFromPoints(point1, point2, point3) {
            let x1 = point2.x - point1.x;
            let y1 = point2.y - point1.y;
            let z1 = point2.z - point1.z;
            let x2 = point3.x - point1.x;
            let y2 = point3.y - point1.y;
            let z2 = point3.z - point1.z;
            let yz = (y1 * z2) - (z1 * y2);
            let xz = (z1 * x2) - (x1 * z2);
            let xy = (x1 * y2) - (y1 * x2);
            let pyth = (Math.sqrt((yz * yz) + (xz * xz) + (xy * xy)));
            let invPyth;
            if (pyth !== 0) {
                invPyth = 1.0 / pyth;
            }
            else {
                invPyth = 0.0;
            }
            this.normal.x = yz * invPyth;
            this.normal.y = xz * invPyth;
            this.normal.z = xy * invPyth;
            this.d = -((this.normal.x * point1.x) + (this.normal.y * point1.y) + (this.normal.z * point1.z));
            return this;
        }
        /**
         * Boolean : True is the vector "direction"  is the same side than the plane normal.
         */
        isFrontFacingTo(direction, epsilon) {
            let dot = Vector3.Dot(this.normal, direction);
            return (dot <= epsilon);
        }
        /**
         * Returns the signed distance (float) from the given point to the Plane.
         */
        signedDistanceTo(point) {
            return Vector3.Dot(point, this.normal) + this.d;
        }
        // Statics
        /**
         * Returns a new Plane from the given array.
         */
        static FromArray(array) {
            return new Plane(array[0], array[1], array[2], array[3]);
        }
        /**
         * Returns a new Plane defined by the three given points.
         */
        static FromPoints(point1, point2, point3) {
            let result = new Plane(0.0, 0.0, 0.0, 0.0);
            result.copyFromPoints(point1, point2, point3);
            return result;
        }
        /**
         * Returns a new Plane the normal vector to this plane at the given origin point.
         * Note : the vector "normal" is updated because normalized.
         */
        static FromPositionAndNormal(origin, normal) {
            let result = new Plane(0.0, 0.0, 0.0, 0.0);
            normal.normalize();
            result.normal = normal;
            result.d = -(normal.x * origin.x + normal.y * origin.y + normal.z * origin.z);
            return result;
        }
        /**
         * Returns the signed distance between the plane defined by the normal vector at the "origin"" point and the given other point.
         */
        static SignedDistanceToPlaneFromPositionAndNormal(origin, normal, point) {
            let d = -(normal.x * origin.x + normal.y * origin.y + normal.z * origin.z);
            return Vector3.Dot(point, normal) + d;
        }
    }
    FIREFLYX.Plane = Plane;
    // tslint:disable-next-line: class-name
    class Rect {
        /**
         * Creates a Viewport object located at (x, y) and sized (width, height).
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(x, y, w, h) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
        }
        // public toGlobal(renderWidthOrEngine: number | Engine, renderHeight: number): Viewport {
        //     if ((<Engine>renderWidthOrEngine).getRenderWidth) {
        //         let engine = (<Engine>renderWidthOrEngine);
        //         return this.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
        //     }
        //     let renderWidth = <number>renderWidthOrEngine;
        //     return new Viewport(this.x * renderWidth, this.y * renderHeight, this.width * renderWidth, this.height * renderHeight);
        // }
        /**
         * Returns a new Rect copied from the current one.
         */
        clone() {
            return new Rect(this.x, this.y, this.w, this.h);
        }
        /**
         * Updates the Rect xywh values from the given floats
         * @param x
         * @param y
         * @param w
         * @param h
         * @returns this
         */
        set(x, y, w, h) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
            return this;
        }
        // Operators
        /**
         * Returns a new array populated with 4 elements.
         */
        asArray() {
            let result = new Array();
            this.toArray(result, 0);
            return result;
        }
        /**
         * Populates the given array from the given index with the Rect.
         * Returns the Vector4.
         */
        toArray(array, index) {
            if (index === undefined) {
                index = 0;
            }
            array[index] = this.x;
            array[index + 1] = this.y;
            array[index + 2] = this.w;
            array[index + 3] = this.h;
            return this;
        }
    }
    FIREFLYX.Rect = Rect;
    class Frustum {
        /**
         * Returns a new array of 6 Frustum planes computed by the given transformation matrix.
         */
        static GetPlanes(transform) {
            let frustumPlanes = [];
            for (let index = 0; index < 6; index++) {
                frustumPlanes.push(new Plane(0.0, 0.0, 0.0, 0.0));
            }
            Frustum.GetPlanesToRef(transform, frustumPlanes);
            return frustumPlanes;
        }
        static GetNearPlaneToRef(transform, frustumPlane) {
            frustumPlane.normal.x = transform.m[3] + transform.m[2];
            frustumPlane.normal.y = transform.m[7] + transform.m[6];
            frustumPlane.normal.z = transform.m[11] + transform.m[10];
            frustumPlane.d = transform.m[15] + transform.m[14];
            frustumPlane.normalize();
        }
        static GetFarPlaneToRef(transform, frustumPlane) {
            frustumPlane.normal.x = transform.m[3] - transform.m[2];
            frustumPlane.normal.y = transform.m[7] - transform.m[6];
            frustumPlane.normal.z = transform.m[11] - transform.m[10];
            frustumPlane.d = transform.m[15] - transform.m[14];
            frustumPlane.normalize();
        }
        static GetLeftPlaneToRef(transform, frustumPlane) {
            frustumPlane.normal.x = transform.m[3] + transform.m[0];
            frustumPlane.normal.y = transform.m[7] + transform.m[4];
            frustumPlane.normal.z = transform.m[11] + transform.m[8];
            frustumPlane.d = transform.m[15] + transform.m[12];
            frustumPlane.normalize();
        }
        static GetRightPlaneToRef(transform, frustumPlane) {
            frustumPlane.normal.x = transform.m[3] - transform.m[0];
            frustumPlane.normal.y = transform.m[7] - transform.m[4];
            frustumPlane.normal.z = transform.m[11] - transform.m[8];
            frustumPlane.d = transform.m[15] - transform.m[12];
            frustumPlane.normalize();
        }
        static GetTopPlaneToRef(transform, frustumPlane) {
            frustumPlane.normal.x = transform.m[3] - transform.m[1];
            frustumPlane.normal.y = transform.m[7] - transform.m[5];
            frustumPlane.normal.z = transform.m[11] - transform.m[9];
            frustumPlane.d = transform.m[15] - transform.m[13];
            frustumPlane.normalize();
        }
        static GetBottomPlaneToRef(transform, frustumPlane) {
            frustumPlane.normal.x = transform.m[3] + transform.m[1];
            frustumPlane.normal.y = transform.m[7] + transform.m[5];
            frustumPlane.normal.z = transform.m[11] + transform.m[9];
            frustumPlane.d = transform.m[15] + transform.m[13];
            frustumPlane.normalize();
        }
        /**
         * Sets the given array "frustumPlanes" with the 6 Frustum planes computed by the given transformation matrix.
         */
        static GetPlanesToRef(transform, frustumPlanes) {
            // Near
            Frustum.GetNearPlaneToRef(transform, frustumPlanes[0]);
            // Far
            Frustum.GetFarPlaneToRef(transform, frustumPlanes[1]);
            // Left
            Frustum.GetLeftPlaneToRef(transform, frustumPlanes[2]);
            // Right
            Frustum.GetRightPlaneToRef(transform, frustumPlanes[3]);
            // Top
            Frustum.GetTopPlaneToRef(transform, frustumPlanes[4]);
            // Bottom
            Frustum.GetBottomPlaneToRef(transform, frustumPlanes[5]);
        }
    }
    FIREFLYX.Frustum = Frustum;
    /** Defines the 3 main axes */
    class Axis {
    }
    /** X axis */
    Axis.X = new Vector3(1.0, 0.0, 0.0);
    /** Y axis */
    Axis.Y = new Vector3(0.0, 1.0, 0.0);
    /** Z axis */
    Axis.Z = new Vector3(0.0, 0.0, 1.0);
    FIREFLYX.Axis = Axis;
    class BezierCurve {
        /**
         * Returns the cubic Bezier interpolated value (float) at "t" (float) from the given x1, y1, x2, y2 floats.
         */
        static interpolate(t, x1, y1, x2, y2) {
            // Extract X (which is equal to time here)
            let f0 = 1 - 3 * x2 + 3 * x1;
            let f1 = 3 * x2 - 6 * x1;
            let f2 = 3 * x1;
            let refinedT = t;
            for (let i = 0; i < 5; i++) {
                let refinedT2 = refinedT * refinedT;
                let refinedT3 = refinedT2 * refinedT;
                let x = f0 * refinedT3 + f1 * refinedT2 + f2 * refinedT;
                let slope = 1.0 / (3.0 * f0 * refinedT2 + 2.0 * f1 * refinedT + f2);
                refinedT -= (x - t) * slope;
                refinedT = Math.min(1, Math.max(0, refinedT));
            }
            // Resolve cubic bezier for the given x
            return 3 * Math.pow(1 - refinedT, 2) * refinedT * y1 +
                3 * (1 - refinedT) * Math.pow(refinedT, 2) * y2 +
                Math.pow(refinedT, 3);
        }
    }
    FIREFLYX.BezierCurve = BezierCurve;
    /**
     * Defines potential orientation for back face culling
     */
    let Orientation;
    (function (Orientation) {
        /**
         * Clockwise
         */
        Orientation[Orientation["CW"] = 0] = "CW";
        /** Counter clockwise */
        Orientation[Orientation["CCW"] = 1] = "CCW";
    })(Orientation = FIREFLYX.Orientation || (FIREFLYX.Orientation = {}));
    /**
     * Defines angle representation
     */
    class Angle {
        /**
         * Creates an Angle object of "radians" radians (float).
         */
        constructor(radians) {
            this._radians = radians;
            if (this._radians < 0.0)
                this._radians += (2.0 * Math.PI);
        }
        /**
         * Get value in degrees
         * @returns the Angle value in degrees (float)
         */
        degrees() {
            return this._radians * 180.0 / Math.PI;
        }
        /**
         * Get value in radians
         * @returns the Angle value in radians (float)
         */
        radians() {
            return this._radians;
        }
        /**
         * Gets a new Angle object valued with the angle value in radians between the two given vectors
         * @param a defines first vector
         * @param b defines second vector
         * @returns a new Angle
         */
        static BetweenTwoPoints(a, b) {
            let delta = b.subtract(a);
            let theta = Math.atan2(delta.y, delta.x);
            return new Angle(theta);
        }
        /**
         * Gets a new Angle object from the given float in radians
         * @param radians defines the angle value in radians
         * @returns a new Angle
         */
        static FromRadians(radians) {
            return new Angle(radians);
        }
        /**
         * Gets a new Angle object from the given float in degrees
         * @param degrees defines the angle value in degrees
         * @returns a new Angle
         */
        static FromDegrees(degrees) {
            return new Angle(degrees * Math.PI / 180.0);
        }
    }
    FIREFLYX.Angle = Angle;
    class Arc2 {
        /**
         * Creates an Arc object from the three given points : start, middle and end.
         */
        constructor(startPoint, midPoint, endPoint) {
            this.startPoint = startPoint;
            this.midPoint = midPoint;
            this.endPoint = endPoint;
            let temp = Math.pow(midPoint.x, 2) + Math.pow(midPoint.y, 2);
            let startToMid = (Math.pow(startPoint.x, 2) + Math.pow(startPoint.y, 2) - temp) / 2.;
            let midToEnd = (temp - Math.pow(endPoint.x, 2) - Math.pow(endPoint.y, 2)) / 2.;
            let det = (startPoint.x - midPoint.x) * (midPoint.y - endPoint.y) - (midPoint.x - endPoint.x) * (startPoint.y - midPoint.y);
            this.centerPoint = new Vector2((startToMid * (midPoint.y - endPoint.y) - midToEnd * (startPoint.y - midPoint.y)) / det, ((startPoint.x - midPoint.x) * midToEnd - (midPoint.x - endPoint.x) * startToMid) / det);
            this.radius = this.centerPoint.subtract(this.startPoint)
                .length();
            this.startAngle = Angle.BetweenTwoPoints(this.centerPoint, this.startPoint);
            let a1 = this.startAngle.degrees();
            let a2 = Angle.BetweenTwoPoints(this.centerPoint, this.midPoint)
                .degrees();
            let a3 = Angle.BetweenTwoPoints(this.centerPoint, this.endPoint)
                .degrees();
            // angles correction
            if (a2 - a1 > +180.0)
                a2 -= 360.0;
            if (a2 - a1 < -180.0)
                a2 += 360.0;
            if (a3 - a2 > +180.0)
                a3 -= 360.0;
            if (a3 - a2 < -180.0)
                a3 += 360.0;
            this.orientation = (a2 - a1) < 0 ? Orientation.CW : Orientation.CCW;
            this.angle = Angle.FromDegrees(this.orientation === Orientation.CW ? a1 - a3 : a3 - a1);
        }
    }
    FIREFLYX.Arc2 = Arc2;
    class Path2 {
        /**
         * Creates a Path2 object from the starting 2D coordinates x and y.
         */
        constructor(x, y) {
            this._points = new Array();
            this._length = 0.0;
            this.closed = false;
            this._points.push(new Vector2(x, y));
        }
        /**
         * Adds a new segment until the given coordinates (x, y) to the current Path2.
         * Returns the updated Path2.
         */
        addLineTo(x, y) {
            if (this.closed) {
                return this;
            }
            let newPoint = new Vector2(x, y);
            let previousPoint = this._points[this._points.length - 1];
            this._points.push(newPoint);
            this._length += newPoint.subtract(previousPoint)
                .length();
            return this;
        }
        /**
         * Adds _numberOfSegments_ segments according to the arc definition
         *  (middle point coordinates, end point coordinates, the arc start point being the current Path2 last point) to the current Path2.
         * Returns the updated Path2.
         */
        addArcTo(midX, midY, endX, endY, numberOfSegments = 36) {
            if (this.closed) {
                return this;
            }
            let startPoint = this._points[this._points.length - 1];
            let midPoint = new Vector2(midX, midY);
            let endPoint = new Vector2(endX, endY);
            let arc = new Arc2(startPoint, midPoint, endPoint);
            let increment = arc.angle.radians() / numberOfSegments;
            if (arc.orientation === Orientation.CW)
                increment *= -1;
            let currentAngle = arc.startAngle.radians() + increment;
            for (let i = 0; i < numberOfSegments; i++) {
                let x = Math.cos(currentAngle) * arc.radius + arc.centerPoint.x;
                let y = Math.sin(currentAngle) * arc.radius + arc.centerPoint.y;
                this.addLineTo(x, y);
                currentAngle += increment;
            }
            return this;
        }
        /**
         * Closes the Path2.
         * Returns the Path2.
         */
        close() {
            this.closed = true;
            return this;
        }
        /**
         * Returns the Path2 total length (float).
         */
        length() {
            let result = this._length;
            if (!this.closed) {
                let lastPoint = this._points[this._points.length - 1];
                let firstPoint = this._points[0];
                result += (firstPoint.subtract(lastPoint)
                    .length());
            }
            return result;
        }
        /**
         * Returns the Path2 internal array of points.
         */
        getPoints() {
            return this._points;
        }
        /**
         * Returns a new Vector2 located at a percentage of the Path2 total length on this path.
         */
        getPointAtLengthPosition(normalizedLengthPosition) {
            if (normalizedLengthPosition < 0 || normalizedLengthPosition > 1) {
                return Vector2.Zero();
            }
            let lengthPosition = normalizedLengthPosition * this.length();
            let previousOffset = 0;
            for (let i = 0; i < this._points.length; i++) {
                let j = (i + 1) % this._points.length;
                let a = this._points[i];
                let b = this._points[j];
                let bToA = b.subtract(a);
                let nextOffset = (bToA.length() + previousOffset);
                if (lengthPosition >= previousOffset && lengthPosition <= nextOffset) {
                    let dir = bToA.normalize();
                    let localOffset = lengthPosition - previousOffset;
                    return new Vector2(a.x + (dir.x * localOffset), a.y + (dir.y * localOffset));
                }
                previousOffset = nextOffset;
            }
            return Vector2.Zero();
        }
        /**
         * Returns a new Path2 starting at the coordinates (x, y).
         */
        static StartingAt(x, y) {
            return new Path2(x, y);
        }
    }
    FIREFLYX.Path2 = Path2;
    class Path3D {
        /**
        * new Path3D(path, normal, raw)
        * Creates a Path3D. A Path3D is a logical math object, so not a mesh.
        * please read the description in the tutorial :  http://doc.babylonjs.com/tutorials/How_to_use_Path3D
        * path : an array of Vector3, the curve axis of the Path3D
        * normal (optional) : Vector3, the first wanted normal to the curve. Ex (0, 1, 0) for a vertical normal.
        * raw (optional, default false) : boolean, if true the returned Path3D isn't normalized. Useful to depict path acceleration or speed.
        */
        constructor(path, firstNormal = null, raw) {
            this.path = path;
            this._curve = new Array();
            this._distances = new Array();
            this._tangents = new Array();
            this._normals = new Array();
            this._binormals = new Array();
            for (let p = 0; p < path.length; p++) {
                this._curve[p] = path[p].clone(); // hard copy
            }
            this._raw = raw || false;
            this._compute(firstNormal);
        }
        /**
         * Returns the Path3D array of successive Vector3 designing its curve.
         */
        getCurve() {
            return this._curve;
        }
        /**
         * Returns an array populated with tangent vectors on each Path3D curve point.
         */
        getTangents() {
            return this._tangents;
        }
        /**
         * Returns an array populated with normal vectors on each Path3D curve point.
         */
        getNormals() {
            return this._normals;
        }
        /**
         * Returns an array populated with binormal vectors on each Path3D curve point.
         */
        getBinormals() {
            return this._binormals;
        }
        /**
         * Returns an array populated with distances (float) of the i-th point from the first curve point.
         */
        getDistances() {
            return this._distances;
        }
        /**
         * Forces the Path3D tangent, normal, binormal and distance recomputation.
         * Returns the same object updated.
         */
        update(path, firstNormal = null) {
            for (let p = 0; p < path.length; p++) {
                this._curve[p].x = path[p].x;
                this._curve[p].y = path[p].y;
                this._curve[p].z = path[p].z;
            }
            this._compute(firstNormal);
            return this;
        }
        // private function compute() : computes tangents, normals and binormals
        _compute(firstNormal) {
            let l = this._curve.length;
            // first and last tangents
            this._tangents[0] = this._getFirstNonNullVector(0);
            if (!this._raw) {
                this._tangents[0].normalize();
            }
            this._tangents[l - 1] = this._curve[l - 1].subtract(this._curve[l - 2]);
            if (!this._raw) {
                this._tangents[l - 1].normalize();
            }
            // normals and binormals at first point : arbitrary vector with _normalVector()
            let tg0 = this._tangents[0];
            let pp0 = this._normalVector(this._curve[0], tg0, firstNormal);
            this._normals[0] = pp0;
            if (!this._raw) {
                this._normals[0].normalize();
            }
            this._binormals[0] = Vector3.Cross(tg0, this._normals[0]);
            if (!this._raw) {
                this._binormals[0].normalize();
            }
            this._distances[0] = 0.0;
            // normals and binormals : next points
            let prev; // previous vector (segment)
            let cur; // current vector (segment)
            let curTang; // current tangent
            // previous normal
            let prevBinor; // previous binormal
            for (let i = 1; i < l; i++) {
                // tangents
                prev = this._getLastNonNullVector(i);
                if (i < l - 1) {
                    cur = this._getFirstNonNullVector(i);
                    this._tangents[i] = prev.add(cur);
                    this._tangents[i].normalize();
                }
                this._distances[i] = this._distances[i - 1] + prev.length();
                // normals and binormals
                // http://www.cs.cmu.edu/afs/andrew/scs/cs/15-462/web/old/asst2camera.html
                curTang = this._tangents[i];
                prevBinor = this._binormals[i - 1];
                this._normals[i] = Vector3.Cross(prevBinor, curTang);
                if (!this._raw) {
                    this._normals[i].normalize();
                }
                this._binormals[i] = Vector3.Cross(curTang, this._normals[i]);
                if (!this._raw) {
                    this._binormals[i].normalize();
                }
            }
        }
        // private function getFirstNonNullVector(index)
        // returns the first non null vector from index : curve[index + N].subtract(curve[index])
        _getFirstNonNullVector(index) {
            let i = 1;
            let nNVector = this._curve[index + i].subtract(this._curve[index]);
            while (nNVector.length() === 0 && index + i + 1 < this._curve.length) {
                i++;
                nNVector = this._curve[index + i].subtract(this._curve[index]);
            }
            return nNVector;
        }
        // private function getLastNonNullVector(index)
        // returns the last non null vector from index : curve[index].subtract(curve[index - N])
        _getLastNonNullVector(index) {
            let i = 1;
            let nLVector = this._curve[index].subtract(this._curve[index - i]);
            while (nLVector.length() === 0 && index > i + 1) {
                i++;
                nLVector = this._curve[index].subtract(this._curve[index - i]);
            }
            return nLVector;
        }
        // private function normalVector(v0, vt, va) :
        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
        // if va is passed, it returns the va projection on the plane orthogonal to vt at the point v0
        _normalVector(v0, vt, va) {
            let normal0;
            let tgl = vt.length();
            if (tgl === 0.0) {
                tgl = 1.0;
            }
            // tslint:disable-next-line: prefer-switch
            if (va === undefined || va === null) {
                let point;
                if (!FIREFLYX.Scalar.WithinEpsilon(Math.abs(vt.y) / tgl, 1.0, FIREFLYX.epsilon)) { // search for a point in the plane
                    point = new Vector3(0.0, -1.0, 0.0);
                }
                else if (!FIREFLYX.Scalar.WithinEpsilon(Math.abs(vt.x) / tgl, 1.0, FIREFLYX.epsilon)) {
                    point = new Vector3(1.0, 0.0, 0.0);
                }
                else if (!FIREFLYX.Scalar.WithinEpsilon(Math.abs(vt.z) / tgl, 1.0, FIREFLYX.epsilon)) {
                    point = new Vector3(0.0, 0.0, 1.0);
                }
                else {
                    point = Vector3.Zero();
                }
                normal0 = Vector3.Cross(vt, point);
            }
            else {
                normal0 = Vector3.Cross(vt, va);
                Vector3.CrossToRef(normal0, vt, normal0);
            }
            normal0.normalize();
            return normal0;
        }
    }
    FIREFLYX.Path3D = Path3D;
    class Curve3 {
        /**
         * A Curve3 object is a logical object, so not a mesh, to handle curves in the 3D geometric space.
         * A Curve3 is designed from a series of successive Vector3.
         * Tuto : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#curve3-object
         */
        constructor(points) {
            this._length = 0.0;
            this._points = points;
            this._length = this._computeLength(points);
        }
        /**
         * Returns a Curve3 object along a Quadratic Bezier curve : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#quadratic-bezier-curve
         * @param v0 (Vector3) the origin point of the Quadratic Bezier
         * @param v1 (Vector3) the control point
         * @param v2 (Vector3) the end point of the Quadratic Bezier
         * @param nbPoints (integer) the wanted number of points in the curve
         */
        static CreateQuadraticBezier(v0, v1, v2, nbPoints) {
            nbPoints = nbPoints > 2 ? nbPoints : 3;
            let bez = new Array();
            let equation = (t, val0, val1, val2) => {
                let res = (1.0 - t) * (1.0 - t) * val0 + 2.0 * t * (1.0 - t) * val1 + t * t * val2;
                return res;
            };
            for (let i = 0; i <= nbPoints; i++) {
                bez.push(new Vector3(equation(i / nbPoints, v0.x, v1.x, v2.x), equation(i / nbPoints, v0.y, v1.y, v2.y), equation(i / nbPoints, v0.z, v1.z, v2.z)));
            }
            return new Curve3(bez);
        }
        /**
         * Returns a Curve3 object along a Cubic Bezier curve : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#cubic-bezier-curve
         * @param v0 (Vector3) the origin point of the Cubic Bezier
         * @param v1 (Vector3) the first control point
         * @param v2 (Vector3) the second control point
         * @param v3 (Vector3) the end point of the Cubic Bezier
         * @param nbPoints (integer) the wanted number of points in the curve
         */
        static CreateCubicBezier(v0, v1, v2, v3, nbPoints) {
            nbPoints = nbPoints > 3 ? nbPoints : 4;
            let bez = new Array();
            let equation = (t, val0, val1, val2, val3) => {
                let res = (1.0 - t) * (1.0 - t) * (1.0 - t) * val0 + 3.0 * t * (1.0 - t) * (1.0 - t) * val1 + 3.0 * t * t * (1.0 - t) * val2 + t * t * t * val3;
                return res;
            };
            for (let i = 0; i <= nbPoints; i++) {
                bez.push(new Vector3(equation(i / nbPoints, v0.x, v1.x, v2.x, v3.x), equation(i / nbPoints, v0.y, v1.y, v2.y, v3.y), equation(i / nbPoints, v0.z, v1.z, v2.z, v3.z)));
            }
            return new Curve3(bez);
        }
        /**
         * Returns a Curve3 object along a Hermite Spline curve : http://doc.babylonjs.com/tutorials/How_to_use_Curve3#hermite-spline
         * @param p1 (Vector3) the origin point of the Hermite Spline
         * @param t1 (Vector3) the tangent vector at the origin point
         * @param p2 (Vector3) the end point of the Hermite Spline
         * @param t2 (Vector3) the tangent vector at the end point
         * @param nbPoints (integer) the wanted number of points in the curve
         */
        static CreateHermiteSpline(p1, t1, p2, t2, nbPoints) {
            let hermite = new Array();
            let step = 1.0 / nbPoints;
            for (let i = 0; i <= nbPoints; i++) {
                hermite.push(Vector3.Hermite(p1, t1, p2, t2, i * step));
            }
            return new Curve3(hermite);
        }
        /**
         * Returns a Curve3 object along a CatmullRom Spline curve :
         * @param points (array of Vector3) the points the spline must pass through. At least, four points required
         * @param nbPoints (integer) the wanted number of points between each curve control points
         * @param closed (boolean) optional with default false, when true forms a closed loop from the points
         */
        static CreateCatmullRomSpline(points, nbPoints, closed) {
            let catmullRom = new Array();
            let step = 1.0 / nbPoints;
            let amount = 0.0;
            if (closed) {
                let pointsCount = points.length;
                for (let i = 0; i < pointsCount; i++) {
                    amount = 0;
                    for (let c = 0; c < nbPoints; c++) {
                        catmullRom.push(Vector3.CatmullRom(points[i % pointsCount], points[(i + 1) % pointsCount], points[(i + 2) % pointsCount], points[(i + 3) % pointsCount], amount));
                        amount += step;
                    }
                }
                catmullRom.push(catmullRom[0]);
            }
            else {
                let totalPoints = new Array();
                totalPoints.push(points[0].clone());
                Array.prototype.push.apply(totalPoints, points);
                totalPoints.push(points[points.length - 1].clone());
                let i = 0;
                for (; i < totalPoints.length - 3; i++) {
                    amount = 0;
                    for (let c = 0; c < nbPoints; c++) {
                        catmullRom.push(Vector3.CatmullRom(totalPoints[i], totalPoints[i + 1], totalPoints[i + 2], totalPoints[i + 3], amount));
                        amount += step;
                    }
                }
                i--;
                catmullRom.push(Vector3.CatmullRom(totalPoints[i], totalPoints[i + 1], totalPoints[i + 2], totalPoints[i + 3], amount));
            }
            return new Curve3(catmullRom);
        }
        /**
         * Returns the Curve3 stored array of successive Vector3
         */
        getPoints() {
            return this._points;
        }
        /**
         * Returns the computed length (float) of the curve.
         */
        length() {
            return this._length;
        }
        /**
         * Returns a new instance of Curve3 object : let curve = curveA.continue(curveB);
         * This new Curve3 is built by translating and sticking the curveB at the end of the curveA.
         * curveA and curveB keep unchanged.
         */
        continue(curve) {
            let lastPoint = this._points[this._points.length - 1];
            let continuedPoints = this._points.slice();
            let curvePoints = curve.getPoints();
            for (let i = 1; i < curvePoints.length; i++) {
                continuedPoints.push(curvePoints[i].subtract(curvePoints[0])
                    .add(lastPoint));
            }
            let continuedCurve = new Curve3(continuedPoints);
            return continuedCurve;
        }
        _computeLength(path) {
            let l = 0;
            for (let i = 1; i < path.length; i++) {
                l += (path[i].subtract(path[i - 1])).length();
            }
            return l;
        }
    }
    FIREFLYX.Curve3 = Curve3;
    // Same as Tmp but not exported to keep it only for math functions to avoid conflicts
    class MathTmp {
    }
    MathTmp.Vector3 = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
    MathTmp.Matrix = [Matrix.Zero(), Matrix.Zero()];
    MathTmp.Quaternion = [Quaternion.Zero(), Quaternion.Zero(), Quaternion.Zero()];
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path = "./Math/math.ts"/>
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 线性变换节点组件
     */
    class Transform extends FIREFLYX.Component {
        constructor() {
            super(...arguments);
            //public field
            //private field
            this._children = [];
            this._localPosition = FIREFLYX.Vector3.Zero();
            this._localRotation = FIREFLYX.Quaternion.Identity();
            this._localScale = FIREFLYX.Vector3.One();
            this._localMatrix = FIREFLYX.Matrix.Identity();
            this._localDirty = false;
            this._position = FIREFLYX.Vector3.Zero();
            this._rotation = FIREFLYX.Quaternion.Identity();
            this._scale = FIREFLYX.Vector3.One();
            this._matrix = FIREFLYX.Matrix.Identity();
            this._dirty = false;
            this._uboBlockSize = 4 * 16;
            this._uboDirty = false;
        }
        /** 事件派发器 */
        get eventDisp() {
            if (!this._eventDisp) {
                this._eventDisp = new FIREFLYX.EventDispatcher();
            }
            return this._eventDisp;
        }
        //get set
        /** 子节点数量 */
        get childCount() { return this._children.length; }
        /** 父节点 */
        get parent() { return this._parent; }
        set parent(val) {
            if (this._parent == val)
                return; //skip same val
            //双向绑定
            this._parent = val;
            if (!val) { //parent 是 null 等于在跟层级
                //为null 解绑
                if (this._parent) {
                    let i = this._parent._children.indexOf(this);
                    if (i != -1) {
                        this._parent._children.splice(i, 1);
                    }
                }
                //entity active 状态处理
                if (this.entity) {
                    this.entity.setActive(this.entity.activeSelf);
                }
            }
            else {
                val._children.push(this);
                //标记脏状态
                this._dirty = true;
                this.dirtyChildren();
                //entity active 状态处理
                if (this.entity && val.entity && val.entity.activeInHierarchy != this.entity.activeInHierarchy) {
                    this.entity.setActive(this.entity.activeSelf);
                }
            }
        }
        /** 节点名 */
        get name() {
            if (!this.entity)
                return "";
            return this.entity.name;
        }
        set name(val) {
            if (!this.entity)
                return;
            this.entity.name = val;
        }
        /** 本地坐标系中的坐标位置 */
        get localPostion() { return this._localPosition; }
        set localPostion(val) {
            if (!val)
                return;
            if (val != this._localPosition) {
                this._localPosition.copyFrom(val);
            }
            //dirty 处理
            this.dirty();
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`localPostion_set`) > 0) {
                this.eventDisp.dispatch(`localPostion_set`, this);
            }
        }
        /** 本地坐标系中的旋转 */
        get localRotation() { return this._localRotation; }
        set localRotation(val) {
            if (!val)
                return;
            if (val != this._localRotation) {
                this._localRotation.copyFrom(val);
            }
            //dirty 处理
            this.dirty();
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`localRotation_set`) > 0) {
                this.eventDisp.dispatch(`localRotation_set`, this);
            }
        }
        /** 本地坐标系中的缩放 */
        get localScale() { return this._localScale; }
        set localScale(val) {
            if (!val)
                return;
            if (val != this._localScale) {
                this._localScale.copyFrom(val);
            }
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`localScale_set`) > 0) {
                this.eventDisp.dispatch(`localScale_set`, this);
            }
            //dirty 处理
            this.dirty();
        }
        /** 世界坐标系中的坐标位置 */
        get position() { return this._position; }
        set position(val) {
            if (!val)
                return;
            if (val != this._position) {
                this._position.copyFrom(val);
            }
            //调整 local
            if (this._parent) {
                //用父节点的逆矩阵算本地坐标系的位置
                let invMatrix = Transform.helpMtx;
                this._parent.getMatrix()
                    .invertToRef(invMatrix);
                FIREFLYX.Vector3.TransformCoordinatesToRef(val, invMatrix, this._localPosition);
            }
            else {
                this._localPosition.copyFrom(val);
            }
            //通过set触发local的修改
            this.localPostion = this._localPosition;
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`postion_set`) > 0) {
                this.eventDisp.dispatch(`postion_set`, this);
            }
        }
        /** 世界坐标系中的旋转 */
        get rotation() { return this._rotation; }
        set rotation(val) {
            if (!val)
                return;
            if (val != this._rotation) {
                this._rotation.copyFrom(val);
            }
            //调整 local
            if (this._parent) {
                let invRotation = Transform.hlepQuat;
                invRotation.copyFrom(this._parent.rotation);
                FIREFLYX.Quaternion.InverseSelf(invRotation);
                invRotation.multiplyToRef(val, this._localRotation);
            }
            else {
                this._localRotation.copyFrom(val);
            }
            //通过set触发local的修改
            this.localRotation = this._localRotation;
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`rotation_set`) > 0) {
                this.eventDisp.dispatch(`rotation_set`, this);
            }
        }
        /** 世界坐标系中的缩放 */
        get scale() { return this._scale; }
        set scale(val) {
            if (!val)
                return;
            if (val != this._scale) {
                this._scale.copyFrom(val);
            }
            //调整 local
            if (this._parent) {
                let pscale = this._parent.scale;
                this._localScale.set(val.x / pscale.x, val.y / pscale.y, val.z / pscale.z);
            }
            else {
                this._localScale.copyFrom(val);
            }
            //通过set触发local的修改
            this.localScale = this._localScale;
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`scale_set`) > 0) {
                this.eventDisp.dispatch(`scale_set`, this);
            }
        }
        /** ubo块byte占用大小 */
        get uboBlockSize() { return this._uboBlockSize; }
        /** model ubo */
        get ubo() {
            let ubo = this._ubo;
            if (!ubo) {
                let gl = FIREFLYX.App.webgl2;
                ubo = this._ubo = gl.createBuffer();
                //bind start
                gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
                gl.bufferData(gl.UNIFORM_BUFFER, this._uboBlockSize, gl.DYNAMIC_DRAW);
                //bind end
                gl.bindBuffer(gl.UNIFORM_BUFFER, null);
                this._uboDirty = true;
            }
            return this._ubo;
        }
        //function
        dispose() {
            super.dispose();
            //子节也做销毁处理
            for (let i = 0, len = this._children.length; i < len; i++) {
                let c = this._children[i];
                c.dispose();
            }
            //注销所有监听
            if (this._eventDisp) {
                this._eventDisp.offAll();
            }
            this._children.length = 0;
            //引用置空
            this._parent = null;
            this._localPosition = null;
            this._localRotation = null;
            this._localScale = null;
            this._localMatrix = null;
            this._localDirty = null;
            this._position = null;
            this._rotation = null;
            this._scale = null;
            this._matrix = null;
            this._eventDisp = null;
        }
        /** 本地坐标系中的变换矩阵 */
        getLocalMatrix() {
            if (this._localDirty) {
                this.unDirtyLocal();
            }
            return this._localMatrix;
        }
        /** 世界坐标系中的变换矩阵 */
        getMatrix() {
            if (this._dirty || this._localDirty) {
                this.unDirty();
            }
            return this._matrix;
        }
        /**
         * 当前节点在世界坐标系中的右方朝向
         * @param result 目标V3
         */
        getRightToRef(result) {
            if (!result)
                return;
            FIREFLYX.Vector3.TransformNormalToRef(Transform.helpV3Right, this.getMatrix(), result);
        }
        /**
         * 当前节点在世界坐标系中的上方朝向
         * @param result 目标V3
         */
        getUpToRef(result) {
            if (!result)
                return;
            FIREFLYX.Vector3.TransformNormalToRef(Transform.helpV3UP, this.getMatrix(), result);
        }
        /**
         * 当前节点在世界坐标系中的前方朝向
         * @param result 目标V3
         */
        getForwardToRef(result) {
            if (!result)
                return;
            FIREFLYX.Vector3.TransformNormalToRef(Transform.helpV3Foward, this.getMatrix(), result);
        }
        /**
         * 看向目标点
         * @param target 目标点
         */
        lookatPoint(target) {
            //看向目标
            let mtx = Transform.helpMtx;
            FIREFLYX.Matrix.LookAtLHToRef(this.position, target, Transform.helpV3UP, mtx);
            let quat = Transform.hlepQuat;
            quat.fromRotationMatrix(mtx);
            FIREFLYX.Quaternion.InverseSelf(quat);
            this.rotation.copyFrom(quat);
            this.rotation = this.rotation;
        }
        /**
         * 获取指定索引的子节点
         * @param index 子节点在队列中的索引
         * @returns 节点对象
         */
        getChild(index) {
            return this._children[index];
        }
        /** 卸下所有的子节点 */
        detachChildren() {
            let cList = this._children;
            if (cList.length < 1)
                return;
            //子节点断开与自己的联系
            for (let i = 0, len = cList.length; i < len; i++) {
                let c = cList[i];
                c._parent = null;
            }
            //清空子节点容器
            cList.length = 0;
        }
        /**
         * 找指定名字的子节点
         * @param name 子节点名
         * @returns 节点对象
         */
        findChild(name) {
            if (name == this.name)
                return this;
            for (let i = 0, len = this._children.length; i < len; i++) {
                let node = this._children[i].findChild(name);
                if (node)
                    return node;
            }
            return null;
        }
        /** 获取当前节点在父节点的孩子队列中的索引值 */
        getSiblingIndex() {
            if (!this._parent)
                return -1;
            return this._parent._children.indexOf(this);
        }
        /**
         * 设置当前节点在父节点的孩子队列中位置
         * @param index 位置索引
         */
        setSiblingIndex(index) {
            if (!this._parent || isNaN(index))
                return;
            let cList = this._parent._children;
            let i = cList.indexOf(this);
            if (i == -1)
                return;
            index = Math.floor(index);
            if (index == i)
                return; //一样的位置
            let insetI = index < 0 ? 0 : index > cList.length - 1 ? cList.length - 1 : index;
            cList.splice(i); //从原来位置移除
            let offset = index < i ? 0 : -1;
            cList.splice(insetI + offset, 0, this); //插入到新的位置
        }
        /**
         * 刷新 ubo 的数据
         * @returns
         */
        refreshUboData() {
            if (!this._uboDirty)
                return;
            const ubo = this.ubo;
            if (!ubo)
                return;
            const gl = FIREFLYX.App.webgl2;
            //bind start
            gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
            //mMatrix
            gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.getMatrix().m);
            //bind end
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
            this._uboDirty = false;
        }
        addListener(eventType, listener, thisArg) {
            this.eventDisp.on(eventType, listener, thisArg);
        }
        removeListener(eventType, listener, thisArg) {
            this.eventDisp.off(eventType, listener, thisArg);
        }
        /** 有变动脏化处理 */
        dirty() {
            if (this._localDirty)
                return; //已经标记过了.
            //自己标记脏状态
            this._uboDirty = this._localDirty = true;
            this.dirtyChildren();
        }
        /** 使子节点们变脏处理 */
        dirtyChildren() {
            this._uboDirty = this._dirty = true;
            //传导到子节点
            for (let i = 0, len = this._children.length; i < len; i++) {
                let c = this._children[i];
                if (c._dirty)
                    continue; //该节点已经标记过了.
                c.dirtyChildren();
            }
        }
        /** 清理脏化状态 */
        unDirty() {
            let localDirty = this._localDirty;
            this.unDirtyLocal();
            if (!localDirty && !this._dirty)
                return;
            //清理脏状态
            this._dirty = false;
            //使用更新计算世界空间矩阵.
            if (!this._parent) {
                this._matrix.copyFrom(this._localMatrix);
            }
            else {
                let pMatrix = this._parent.getMatrix();
                this._localMatrix.multiplyToRef(pMatrix, this._matrix);
            }
            //更新 世界坐标系的 位置、旋转、缩放
            this._matrix.decompose(this._scale, this._rotation, this._position);
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`position_set`) > 0) {
                this.eventDisp.dispatch(`position_set`, this);
            }
            if (this.eventDisp && this.eventDisp.listenerCount(`rotation_set`) > 0) {
                this.eventDisp.dispatch(`rotation_set`, this);
            }
            if (this.eventDisp && this.eventDisp.listenerCount(`scale_set`) > 0) {
                this.eventDisp.dispatch(`scale_set`, this);
            }
        }
        /** 清理脏化本地坐标系状态,同步本地坐标系矩阵 */
        unDirtyLocal() {
            if (!this._localDirty)
                return;
            //清理local 脏状态
            this._localDirty = false;
            //使用更新计算本地空间矩阵.
            FIREFLYX.Matrix.ComposeToRef(this._localScale, this._localRotation, this._localPosition, this._localMatrix);
        }
    }
    //private static field
    Transform.helpV3Right = FIREFLYX.Vector3.Right();
    Transform.helpV3UP = FIREFLYX.Vector3.Up();
    Transform.helpV3Foward = FIREFLYX.Vector3.Forward();
    Transform.helpMtx = new FIREFLYX.Matrix();
    Transform.hlepQuat = new FIREFLYX.Quaternion();
    FIREFLYX.Transform = Transform;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 投影类型 */
    let ProjectionType;
    (function (ProjectionType) {
        /** 透视 */
        ProjectionType[ProjectionType["PERSPECTIVE"] = 0] = "PERSPECTIVE";
        /** 正交 */
        ProjectionType[ProjectionType["ORTHOGRAPHIC"] = 1] = "ORTHOGRAPHIC";
    })(ProjectionType = FIREFLYX.ProjectionType || (FIREFLYX.ProjectionType = {}));
    /** FOV 轴向 */
    let FOVAxis;
    (function (FOVAxis) {
        FOVAxis[FOVAxis["VERTICAL"] = 0] = "VERTICAL";
        FOVAxis[FOVAxis["HORIZONTAL"] = 1] = "HORIZONTAL";
    })(FOVAxis = FIREFLYX.FOVAxis || (FIREFLYX.FOVAxis = {}));
    /** 相机清理模式 */
    let CameraClearMode;
    (function (CameraClearMode) {
        /** 天空盒 */
        CameraClearMode[CameraClearMode["SKY_BOX"] = 0] = "SKY_BOX";
        /** 单色 */
        CameraClearMode[CameraClearMode["COLOR"] = 1] = "COLOR";
        /** 仅清理深度 */
        CameraClearMode[CameraClearMode["DEPTH_ONLY"] = 2] = "DEPTH_ONLY";
        /** 不做任何清理操作 */
        CameraClearMode[CameraClearMode["NONE"] = 3] = "NONE";
    })(CameraClearMode = FIREFLYX.CameraClearMode || (FIREFLYX.CameraClearMode = {}));
    /** 相机 */
    class Camera extends FIREFLYX.Component {
        constructor() {
            super();
            /**
             * 远裁剪面
             */
            this.far = 1000;
            /**
             * 近裁剪面
             */
            this.near = 0.01;
            /**
             * 视口矩形 ViewPort Rect
             */
            this.viewPortRect = new FIREFLYX.Rect(0, 0, 1, 1);
            /** Projection 投影模式 */
            this.projection = ProjectionType.PERSPECTIVE;
            /** 透视投影-Field of View (角度制)*/
            this.fov = 60 * FIREFLYX.toRadian;
            /** 相机 fov  轴向*/
            this.fovAxis = FOVAxis.VERTICAL;
            /** 正交投影 -正交 的视野size */
            this.size = 5;
            /** 背景颜色 */
            this.backGround = new FIREFLYX.Color4(0.18, 0.3, 0.472, 1);
            /** 清理模式 */
            this.clearMode = CameraClearMode.SKY_BOX;
            //view矩阵
            this._viewMatrix = new FIREFLYX.Matrix();
            //投影矩阵
            this._projectionMatrix = new FIREFLYX.Matrix();
            //视窗的 asp 值
            this._asp = 1;
            //init ubo
            const gl = FIREFLYX.App.webgl2;
            this._ubo = gl.createBuffer();
            //bind start
            gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
            const camBlockSize = this._uboBlockSize = 4 * (16 + 16 + 4); //初始化 buffer 设置大小 4 = float
            gl.bufferData(gl.UNIFORM_BUFFER, camBlockSize, gl.DYNAMIC_DRAW);
            //bind end
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
            //
            this._posF32arr = new Float32Array(3);
        }
        /** 相机的 ubo对象 ，用于存储相机的设置参数 */
        get ubo() { return this._ubo; }
        /** ubo块byte占用大小 */
        get uboBlockSize() { return this._uboBlockSize; }
        /** aspect 值 w / h */
        get asp() { return this._asp; }
        /** 相机渲染输出目标纹理 */
        get targetTexture() { return this._targetTexture; }
        set targetTexture(val) {
            if (val == this._targetTexture)
                return;
            this._targetTexture = val;
        }
        /**
         * 刷新 ubo 的数据
         * @returns
         */
        refreshUboData() {
            if (!this.entity)
                return;
            const gl = FIREFLYX.App.webgl2;
            //bind start
            gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
            //ViewMatrix
            gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.getViewMatrix().m);
            //ProjectionMatrx
            gl.bufferSubData(gl.UNIFORM_BUFFER, 4 * 16, this.getProjectionMatrx().m);
            //eyePostion
            const posF32 = this._posF32arr;
            const pos = this.entity.transform.position;
            posF32[0] = pos.x;
            posF32[1] = pos.y;
            posF32[2] = pos.z;
            gl.bufferSubData(gl.UNIFORM_BUFFER, 4 * 32, posF32);
            //bind end
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
        }
        /** 刷新 相机 asp */
        reflashASP() {
            //canvas 尺寸
            let w = FIREFLYX.App.canvasElement.width;
            let h = FIREFLYX.App.canvasElement.height;
            //viewPort Rect设置
            let vp = this.viewPortRect;
            w *= vp.w;
            h *= vp.h;
            this._asp = w / h;
            if (isNaN(this._asp))
                this._asp = 1;
        }
        /** 获取view 矩阵 */
        getViewMatrix() {
            if (this.entity) {
                this.entity.transform.getMatrix()
                    .invertToRef(this._viewMatrix);
            }
            return this._viewMatrix;
        }
        /** 获取投影矩阵 */
        getProjectionMatrx() {
            switch (this.projection) {
                case ProjectionType.PERSPECTIVE:
                    this.calcPerspectiveProjMat();
                    break;
                case ProjectionType.ORTHOGRAPHIC:
                    this.calcOrthographicProjMat();
                    break;
                default:
            }
            return this._projectionMatrix;
        }
        /**
        * 计算 相机视锥 顶点数据
        * @param near 锥体近平面
        * @param far 锥体远平面
        * @param outFrustum 视锥顶点数据列表(8个顶点位置坐标)
        */
        calcFrustumPoints(near, far, outFrustum) {
            if (!this.entity || !outFrustum)
                return;
            if (outFrustum.length < 8) {
                console.warn(` outFrustum array length less than 8.`);
                return;
            }
            //
            const fovIsH = this.fovAxis == FOVAxis.HORIZONTAL;
            const fov = this.fov;
            const asp = this.asp;
            // const near = cam.near;
            // const far = cam.far;
            const toWorldMat = this.entity.transform.getMatrix();
            // const toWorldMat = light.entity.transform.getMatrix();
            //通过 fov + near 求 rect 宽度
            let rhFov = fov * 0.5;
            let nHelfW = 0, nHelfH = 0, fHelfW = 0, fHelfH = 0;
            const tanVal = Math.tan(rhFov);
            if (fovIsH) {
                //near 平面
                nHelfW = tanVal * near;
                nHelfH = nHelfW / asp;
                //远 平面
                fHelfW = tanVal * far;
                fHelfH = fHelfW / asp;
            }
            else {
                nHelfH = tanVal * near;
                nHelfW = nHelfH * asp;
                //
                fHelfH = tanVal * far;
                fHelfW = fHelfH * asp;
            }
            //
            outFrustum[0].set(-nHelfW, nHelfH, near); //n_0
            outFrustum[1].set(nHelfW, nHelfH, near); //n_1
            outFrustum[2].set(nHelfW, -nHelfH, near); //n_2
            outFrustum[3].set(-nHelfW, -nHelfH, near); //n_3
            outFrustum[4].set(-fHelfW, fHelfH, far); //f_0
            outFrustum[5].set(fHelfW, fHelfH, far); //f_1
            outFrustum[6].set(fHelfW, -fHelfH, far); //f_2
            outFrustum[7].set(-fHelfW, -fHelfH, far); //f_3
            outFrustum.forEach((v) => {
                FIREFLYX.Vector3.TransformCoordinatesToRef(v, toWorldMat, v);
            });
        }
        /** 计算透视投影 */
        calcPerspectiveProjMat() {
            FIREFLYX.Matrix.PerspectiveFovLHToRef(this.fov, this._asp, this.near, this.far, this._projectionMatrix, this.fovAxis == FOVAxis.VERTICAL);
        }
        /** 计算正交投影 */
        calcOrthographicProjMat() {
            const w = this.size * this._asp;
            const h = this.size;
            FIREFLYX.Matrix.OrthoLHToRef(w, h, this.near, this.far, this._projectionMatrix);
        }
    }
    //相机标记
    Camera._useSign = "camera";
    FIREFLYX.Camera = Camera;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * buffer 编码 转换为utf8 字符串
     * @param bytes buffer
     * @returns
     */
    function decodeUtf8(bytes) {
        let encoded = "";
        for (let i = 0, len = bytes.length; i < len; i++) {
            let code16 = bytes[i].toString(16);
            encoded += "%" + (code16.length == 2 ? code16 : "0" + code16);
        }
        return decodeURIComponent(encoded);
    }
    FIREFLYX.decodeUtf8 = decodeUtf8;
    const CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
    const uuidTempArr = new Array(36);
    /** 获取一个UUID */
    function makeUUID() {
        let chars = CHARS, uuid = uuidTempArr, rnd = 0, r;
        for (let i = 0; i < 36; i++) {
            if (i == 8 || i == 13 || i == 18 || i == 23) {
                uuid[i] = "-";
            }
            else if (i == 14) {
                uuid[i] = "4";
            }
            else {
                if (rnd <= 0x02)
                    rnd = 0x2000000 + (Math.random() * 0x1000000) | 0;
                r = rnd & 0xf;
                rnd = rnd >> 4;
                uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
            }
        }
        return uuid.join("");
    }
    FIREFLYX.makeUUID = makeUUID;
    /**
         * 获取文件的名字通过资源路径
         * @param path 资源路径
         * @returns 文件的名字
         */
    function GetFileName(path) {
        if (!path)
            return ``;
        let idx = path.indexOf(`.`);
        if (idx == -1)
            return ``;
        let sub = path.substring(0, idx);
        if (!sub)
            return ``;
        sub = sub.replace(`\\`, `/`);
        idx = sub.lastIndexOf(`/`);
        if (idx == -1)
            return ``;
        return sub.substring(idx + 1);
    }
    FIREFLYX.GetFileName = GetFileName;
    /**
     * 获取 文件 原始后缀
     * @param path 资源路径
     * @returns 原始后缀
     */
    function GetFileRawSuffix(path) {
        if (!path)
            return ``;
        let idx = path.lastIndexOf(`.`);
        if (idx == -1)
            return ``;
        return path.substring(idx + 1);
    }
    FIREFLYX.GetFileRawSuffix = GetFileRawSuffix;
    /**
     * 获取 文件 目录路径
     * @param path 资源路径
     * @returns 目录路径
     */
    function GetFileFolderPath(path) {
        if (!path)
            return ``;
        path = path.replace(`\\`, `/`);
        let idx = path.indexOf(`/`);
        if (idx == -1)
            return ``;
        return path.substring(0, idx);
    }
    FIREFLYX.GetFileFolderPath = GetFileFolderPath;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="../Util/tool.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** 资源基础类 */
    class Asset {
        constructor(name = "", uuid) {
            this._isDisposed = false;
            this.name = name;
            if (uuid == null) {
                this.UUID = FIREFLYX.makeUUID();
            }
            else {
                this.UUID = uuid;
            }
        }
        /** 是否被销毁 */
        get isDisposed() { return this._isDisposed; }
        /** 资源释放销毁 */
        dispose() {
            this._isDisposed = true;
        }
    }
    FIREFLYX.Asset = Asset;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./asset.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    class AssetBundle extends FIREFLYX.Asset {
        setData(abPath, resList) {
            //
        }
        getAsset(fileSign) {
            //
            return null;
        }
        /**
         * 根据 资源类型 获取所有资源
         * @param resType 资源类型
         * @param out 资源返回的容器
         */
        getAssetArray(resType, out) {
            //
        }
    }
    FIREFLYX.AssetBundle = AssetBundle;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    // export interface IAsset {
    //     /** 获取资源ID */
    //     ID: number;
    //     /** 获取资源名 */
    //     name: string;
    //     /** 是否被销毁 */
    //     isDisposed: boolean;
    // }
    /** 资源类型 */
    let AssetType;
    (function (AssetType) {
        AssetType[AssetType["ERROR"] = 0] = "ERROR";
        AssetType[AssetType["BIN"] = 1] = "BIN";
        AssetType[AssetType["MESH"] = 2] = "MESH";
        AssetType[AssetType["OBJ_MESH"] = 3] = "OBJ_MESH";
        AssetType[AssetType["TEXTURE"] = 4] = "TEXTURE";
        AssetType[AssetType["HTML_IMAGE"] = 5] = "HTML_IMAGE";
        AssetType[AssetType["PREFAB"] = 6] = "PREFAB";
        AssetType[AssetType["GLTF"] = 7] = "GLTF";
        AssetType[AssetType["GLB"] = 8] = "GLB";
        AssetType[AssetType["ASSET_BUNDLE"] = 9] = "ASSET_BUNDLE";
        AssetType[AssetType["GLSL"] = 10] = "GLSL";
        AssetType[AssetType["SHADER"] = 11] = "SHADER";
        AssetType[AssetType["MATERIAL"] = 12] = "MATERIAL";
    })(AssetType = FIREFLYX.AssetType || (FIREFLYX.AssetType = {}));
    /** 资源解析时传递参数Map */
    // eslint-disable-next-line @typescript-eslint/naming-convention
    FIREFLYX.ResPathProcessorParameterMap = new Map();
    /** 资源管理器 */
    class AssetManager {
        static init() {
            //proccessor 注册
            this.RegProcessor(new FIREFLYX.HtmlImageProcessor());
            this.RegProcessor(new FIREFLYX.PngTexProcessor());
            this.RegProcessor(new FIREFLYX.JpgTexProcessor());
            this.RegProcessor(new FIREFLYX.WebpTexProcessor());
            this.RegProcessor(new FIREFLYX.OBJMeshProcessor());
            this.RegProcessor(new FIREFLYX.BinProcessor());
            this.RegProcessor(new FIREFLYX.GLTFProcessor());
            this.RegProcessor(new FIREFLYX.GLBProcessor());
            this.RegProcessor(new FIREFLYX.ShaderProcessor());
            this.RegProcessor(new FIREFLYX.GLSLProcessor());
        }
        /**
         * 获取文件的引擎资源类型
         * @param path 文件资源路径
         * @returns 引擎资源类型
         */
        static GetFileType(path) {
            let temp = this.SuffixPattern.exec(path);
            if (!temp || temp.length < 0)
                return AssetType.ERROR;
            let fullSuffix = temp[0]; //可能是 双后缀(.xx) 或 单后缀(.xx.yy)
            let lastSuffix = fullSuffix; //单后缀(.xx.yy)
            let result = this.SuffixMap[fullSuffix];
            //.xx.*  使用* 模式的 双后缀
            if (result == null && temp.length > 1) {
                lastSuffix = temp[1];
                let startSuffix = fullSuffix.replace(lastSuffix, `.*`);
                result = this.SuffixMap[startSuffix];
            }
            //双后缀 没匹配到，尝试取 单后缀
            if (result == null && lastSuffix) {
                result = this.SuffixMap[lastSuffix];
            }
            //最后也没找到 标记为 error
            if (result == null)
                return AssetType.ERROR;
            return result;
        }
        /**
         * 异步加载单文件资源通过路径
         * @param path 文件路径
         * @returns 资源
         */
        static async LoadFile(path) {
            //注：加载开多线，加载完排入队列，逐个解析
            //先找缓存看是否存在
            if (this.CacheResMap[path]) {
                return this.CacheResMap[path];
            }
            let fType = this.GetFileType(path);
            if (fType == AssetType.ERROR) {
                //文件类型不支持
                console.error(`file :${path} , type not supported!`);
                return null;
            }
            //加载中处理
            if (this.CacheResMap[path]) {
                let _wait2Loaded = new Promise((res, rej) => {
                    let obj = {
                        loaded: () => { res(this.CacheResMap[path]); },
                        loadFail: () => { rej(new Error(`file loadFail of path : ${path}`)); },
                        unReg: () => {
                            FIREFLYX.GlobalEventManager.Instance.removeListener("assetLoaded", obj.loaded, obj);
                            FIREFLYX.GlobalEventManager.Instance.removeListener("assetLoadFail", obj.loadFail, obj);
                        },
                    };
                    FIREFLYX.GlobalEventManager.Instance.addListener("assetLoaded", obj.loaded, obj);
                    FIREFLYX.GlobalEventManager.Instance.addListener("assetLoadFail", obj.loadFail, obj);
                });
                //返回 等候结果的 Promise
                return _wait2Loaded;
            }
            //文件名
            let fileName = FIREFLYX.GetFileName(path);
            //加载文件资源数据
            let proccessor = this.ParserMap[fType];
            this.LoadingResMap[path] = true;
            let data = await FIREFLYX.FileLoad.xhr(path, proccessor.rawType);
            //是image类型等待image的load结束
            if (proccessor.rawType == "blob" && data.type.substring(0, 5) == "image") {
                // let imgP = new Promise<HTMLImageElement>((res, rej) => {
                //     let img = new Image();
                //     img.src = window.URL.createObjectURL(data);
                //     img.onload = () => { res(img); };
                //     img.onerror = (err) => { rej(err); };
                // });
                let imgP = FIREFLYX.FileLoad.loadImage(window.URL.createObjectURL(data));
                data = await imgP;
            }
            //根据文件后缀判定资源类型
            //解析资源
            let asset = this.ParseRaw(data, fType, path);
            //清理加载中标记
            delete this.LoadingResMap[path];
            if (asset) {
                asset.name = fileName;
                //资源对象不为空，成功
                this.CacheResMap[path] = asset;
                //event dispath
                if (FIREFLYX.GlobalEventManager.Instance.listenerCount("assetLoaded") > 0) {
                    FIREFLYX.GlobalEventManager.Instance.dispatchEvent("assetLoaded", path);
                }
            }
            else {
                //event dispath
                if (FIREFLYX.GlobalEventManager.Instance.listenerCount("assetLoadFail") > 0) {
                    FIREFLYX.GlobalEventManager.Instance.dispatchEvent("assetLoadFail", path);
                }
            }
            return asset;
        }
        /**
         * 解析资源
         * @param data 资源数据
         * @param fType 资源类型
         * @param filePath 资源文件路径
         * @returns 资源
         */
        static ParseRaw(data, fType, filePath) {
            let proccessor = this.ParserMap[fType];
            let asset = proccessor.parse(data, filePath);
            return asset;
        }
        /**
         * 获取已缓存资源
         * @param resKey 文件路径、标识字符串
         */
        static GetAsset(resKey) {
            return this.CacheResMap[resKey];
        }
        /**
         * 添加设置资源
         * @param resKey 资源名(文件路径、标识字符串)
         * @param res 资源对象(为空表示从容器移除)
         */
        static SetAsset(resKey, res) {
            if (!resKey)
                return;
            if (res) {
                this.CacheResMap[resKey] = res;
            }
            else {
                delete this.CacheResMap[resKey];
            }
        }
        /**
         * 注册资源处理器
         * @param processor 资源处理器
         */
        static RegProcessor(processor) {
            this.ParserMap[processor.Type] = processor;
            this.SuffixMap[processor.suffix] = processor.Type;
        }
    }
    //资源加载完毕的缓存Map
    AssetManager.CacheResMap = {};
    //加载中的资源Map
    AssetManager.LoadingResMap = {};
    //匹配后缀
    AssetManager.SuffixPattern = /(\.([a-z|0-9]*)){1,2}$/;
    AssetManager.ParserMap = {};
    AssetManager.SuffixMap = {};
    FIREFLYX.AssetManager = AssetManager;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./asset.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    class BinaryData extends FIREFLYX.Asset {
    }
    FIREFLYX.BinaryData = BinaryData;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    let DefMesh;
    (function (DefMesh) {
        /** 正方体 */
        DefMesh[DefMesh["CUBE"] = 0] = "CUBE";
        /** 正四边形 */
        DefMesh[DefMesh["QUAD"] = 1] = "QUAD";
        /** 铺满整个裁剪空间的四边形 */
        DefMesh[DefMesh["QUAD_FULL_CLIP"] = 2] = "QUAD_FULL_CLIP";
        /** 正球体 */
        DefMesh[DefMesh["SPHERE"] = 3] = "SPHERE";
        /** 圆柱体体 */
        DefMesh[DefMesh["CYLINDER"] = 4] = "CYLINDER";
    })(DefMesh = FIREFLYX.DefMesh || (FIREFLYX.DefMesh = {}));
    let DefTexture;
    (function (DefTexture) {
        /** 白色纹理 */
        DefTexture[DefTexture["WHITE"] = 0] = "WHITE";
        /** 灰色纹理 */
        DefTexture[DefTexture["GRAY"] = 1] = "GRAY";
        /** 黑色纹理 */
        DefTexture[DefTexture["BLACK"] = 2] = "BLACK";
        /** 黑白网格纹理 */
        DefTexture[DefTexture["GRID"] = 3] = "GRID";
        /** 切线空间法线纹理 */
        DefTexture[DefTexture["NORMAL"] = 4] = "NORMAL";
    })(DefTexture = FIREFLYX.DefTexture || (FIREFLYX.DefTexture = {}));
    let DefMaterial;
    (function (DefMaterial) {
        DefMaterial[DefMaterial["MODEL"] = 0] = "MODEL";
        DefMaterial[DefMaterial["SHADOW_MAP"] = 1] = "SHADOW_MAP";
    })(DefMaterial = FIREFLYX.DefMaterial || (FIREFLYX.DefMaterial = {}));
    /** 内建默认资源 */
    class DefaultAsset {
        /** glsl 库 代码字典容器 */
        static get glslLibCodeMap() { return this._glslLibCodeMap; }
        /** 内建 着色器生成器 字典容器 */
        static get shaderGenMap() { return this._shaderGenMap; }
        /**
         * 获取mesh
         * @param meshType mesh类型
         * @returns mesh
         */
        static GetMesh(meshType) {
            let mesh = this.defMesh[meshType];
            if (!mesh) {
                mesh = new FIREFLYX.Mesh();
                mesh.name = `DefMesh_${DefMesh[meshType]}`;
                let mData = null;
                //初始化资源
                switch (meshType) {
                    case DefMesh.QUAD:
                        mData = this.GenQuadData(1);
                        break;
                    case DefMesh.QUAD_FULL_CLIP:
                        mData = this.GenFullClipQuadData();
                        break;
                    case DefMesh.CUBE:
                        mData = this.GenCubeData(1);
                        break;
                    case DefMesh.SPHERE:
                        mData = this.GenSphereData(0.5, 40, 20);
                        break;
                    case DefMesh.CYLINDER:
                        mData = this.GenCylinderData(2, 0.5, 128);
                        break;
                    default:
                }
                //设置data
                mData === null || mData === void 0 ? void 0 : mData.calcPosBoundary();
                mesh.data = mData;
                //应用参数
                mesh.apply();
                this.defMesh[meshType] = mesh;
            }
            return mesh;
        }
        /**
         * 获取纹理
         * @param textureType 纹理类型
         * @returns 纹理
         */
        static GetTexture(textureType) {
            let texture = this.defTexture[textureType];
            if (!texture) {
                texture = new FIREFLYX.Texture2D(`DefTex_${DefTexture[textureType]}`);
                //初始化资源
                switch (textureType) {
                    case DefTexture.WHITE:
                        this.SetTexOneColor(texture, new FIREFLYX.Color4(1, 1, 1, 1));
                        break;
                    case DefTexture.GRAY:
                        this.SetTexOneColor(texture, new FIREFLYX.Color4(0.5, 0.5, 0.5, 1));
                        break;
                    case DefTexture.BLACK:
                        this.SetTexOneColor(texture, new FIREFLYX.Color4(0, 0, 0, 1));
                        break;
                    case DefTexture.GRID:
                        this.SetTexGrid(texture);
                        texture.paramSetting.mipmap = true;
                        break;
                    case DefTexture.NORMAL:
                        this.SetTexOneColor(texture, new FIREFLYX.Color4(0.5, 0.5, 1, 1));
                        break;
                    default:
                }
                //应用参数
                texture.apply();
                this.defTexture[textureType] = texture;
            }
            return texture;
        }
        /**
         * 获取 glsl 库
         * @param libName 库名
         * @returns glsl
         */
        static getGLSLLib(libName) {
            let result = this.defGlslLib[libName];
            if (!result) {
                //
                let libCode = this._glslLibCodeMap[libName];
                if (libCode) {
                    result = this.defGlslLib[libName] = new FIREFLYX.GLSL();
                    result.setCode(libCode);
                }
            }
            return result;
        }
        /**
         * 获取着色器
         * @param shaderKey 着色器key
         * @returns 着色器
         */
        static GetShader(shaderKey) {
            let result = this.defShader[shaderKey];
            if (!result) {
                const shaderGen = this._shaderGenMap[shaderKey];
                if (shaderGen != null) {
                    result = this.defShader[shaderKey] = shaderGen();
                }
            }
            return result;
        }
        /**
         * 获取默认材质
         * @returns
         */
        static GetMaterial(materialType) {
            let mtr = this.defMaterial[materialType];
            if (!mtr) {
                let shKey = "";
                switch (materialType) {
                    case DefMaterial.MODEL:
                        shKey = FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR;
                        break;
                    case DefMaterial.SHADOW_MAP:
                        shKey = FIREFLYX.BUILTIN_SHADER_BASE_SHADOW_MAP;
                        break;
                    default:
                }
                if (shKey) {
                    mtr = this.defMaterial[materialType] = new FIREFLYX.Material(DefMaterial[materialType]);
                    mtr.shader = this.GetShader(shKey);
                    mtr.lockModify = true;
                }
            }
            return this.defMaterial[materialType];
        }
        /**
         * 生成一个 方片 模型数据
         * @param size 尺寸
         * @returns 网格数据
         */
        static GenQuadData(size = 1) {
            let half = size / 2;
            let data = new FIREFLYX.MeshData();
            let poss = [];
            let uvs = [];
            let triindexs = [];
            let normals = [];
            //set data
            //   0 -------- 1
            //     |      |
            //     |      |
            //   3 -------- 2
            //
            poss.push(-half, half, 0, half, half, 0, half, -half, 0, -half, -half, 0);
            normals.push(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            triindexs.push(0, 3, 1, 1, 3, 2);
            //
            data.posArr = new Float32Array(poss);
            data.normalArr = new Float32Array(normals);
            data.uvArr = new Float32Array(uvs);
            data.triIndexArr = new Uint8Array(triindexs);
            return data;
        }
        /**
         * 生成一个 post 方片 模型数据
         * @returns 网格数据
         */
        static GenFullClipQuadData() {
            let half = 1;
            let data = new FIREFLYX.MeshData();
            let poss = [];
            let uvs = [];
            let triindexs = [];
            // let normals: number[] = [];
            //set data
            //   0 -------- 1
            //     |      |
            //     |      |
            //   3 -------- 2
            //
            poss.push(-half, half, 0, half, half, 0, half, -half, 0, -half, -half, 0);
            // normals.push(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            triindexs.push(0, 3, 1, 1, 3, 2);
            //
            data.posArr = new Float32Array(poss);
            data.uvArr = new Float32Array(uvs);
            data.triIndexArr = new Uint8Array(triindexs);
            // data.normalArr = normals;
            return data;
        }
        /**
         * 生成一个 cube 模型数据
         * @param size 大小尺寸
         * @returns 网格数据
         */
        static GenCubeData(size = 1) {
            let half = size * 0.5;
            let data = new FIREFLYX.MeshData();
            let poss = [];
            let uvs = [];
            let triindexs = [];
            let normals = [];
            //front
            this.fillIndexQuad(triindexs, poss.length);
            poss.push(half, half, half, -half, half, half, -half, -half, half, half, -half, half);
            normals.push(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            //back
            this.fillIndexQuad(triindexs, poss.length);
            poss.push(-half, half, -half, half, half, -half, half, -half, -half, -half, -half, -half);
            normals.push(0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            //top
            this.fillIndexQuad(triindexs, poss.length);
            poss.push(-half, half, half, half, half, half, half, half, -half, -half, half, -half);
            normals.push(0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            //bottom
            this.fillIndexQuad(triindexs, poss.length);
            poss.push(-half, -half, -half, half, -half, -half, half, -half, half, -half, -half, half);
            normals.push(0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            //left
            this.fillIndexQuad(triindexs, poss.length);
            poss.push(-half, half, half, -half, half, -half, -half, -half, -half, -half, -half, half);
            normals.push(-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            //right
            this.fillIndexQuad(triindexs, poss.length);
            poss.push(half, half, -half, half, half, half, half, -half, half, half, -half, -half);
            normals.push(1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0);
            uvs.push(0, 1, 1, 1, 1, 0, 0, 0);
            //
            data.posArr = new Float32Array(poss);
            data.normalArr = new Float32Array(normals);
            data.uvArr = new Float32Array(uvs);
            data.triIndexArr = new Uint8Array(triindexs);
            return data;
        }
        /**
         * 生成一个 圆柱体数据
         * @param height 圆柱体棱角数量
         * @param radius 圆柱高度
         * @param edgeNum 园半径
         * @returns 网格数据
         */
        static GenCylinderData(height = 2, radius = 0.5, edgeNum = 16) {
            let halfH = height / 2;
            let stepAngle = 2 * Math.PI / edgeNum;
            let data = new FIREFLYX.MeshData();
            let poss = [];
            let uvs = [];
            let normals = [];
            let triindexs = [];
            //top center
            poss.push(0, halfH, 0);
            //bottom center
            poss.push(0, -halfH, 0);
            //uv center
            uvs.push(0.5, 0.5, 0.5, 0.5);
            //normal center
            normals.push(0, 1, 0, 0, -1, 0); //Top & bottom normal
            let posGap = 4;
            let posGapHalf = posGap / 2;
            let centerPosNum = 2;
            for (let i = 0, len = edgeNum + 1; i < len; i++) {
                let angle = stepAngle * i;
                let _cos = Math.cos(angle);
                let _sin = Math.sin(angle);
                let x = _cos * radius;
                let z = _sin * radius;
                //pos
                poss.push(x, halfH, z); //Top pos
                poss.push(x, -halfH, z); //bottom pos
                poss.push(x, halfH, z); //top around pos
                poss.push(x, -halfH, z); //bottom around pos
                //uv
                let u = x / 2 + 0.5;
                let v = z / 2 + 0.5;
                let au = i / edgeNum;
                uvs.push(u, v); //Top uv
                uvs.push(u, v); //bottom uv
                uvs.push(au, 0); //Top around uv
                uvs.push(au, 1); //bottom around uv
                //normal
                normals.push(0, 1, 0); //Top normal
                normals.push(0, -1, 0); //bottom normal
                normals.push(x, 0, z); //around normal 0
                normals.push(x, 0, z); //around normal 1
                if (i >= edgeNum)
                    break;
                let step = i * posGap;
                let tStart = step + centerPosNum;
                //top bottom idxs
                let _p0 = tStart;
                let _p1 = tStart + posGap;
                let _p2 = tStart + 1;
                let _p3 = tStart + posGap + 1;
                triindexs.push(0, _p0, _p1); //top tri
                triindexs.push(1, _p3, _p2); //bottom tri
                //around idxs
                let _pa0 = _p0 + posGapHalf;
                let _pa1 = _p1 + posGapHalf;
                let _pa2 = _p2 + posGapHalf;
                let _pa3 = _p3 + posGapHalf;
                triindexs.push(_pa0, _pa2, _pa1); //around tri 0
                triindexs.push(_pa1, _pa2, _pa3); //around tri 1
            }
            data.posArr = new Float32Array(poss);
            data.uvArr = new Float32Array(uvs);
            data.normalArr = new Float32Array(normals);
            data.triIndexArr = new Uint16Array(triindexs);
            return data;
        }
        /**
         * 生成一个球形mesh
         * @param size 尺寸（半径）
         * @param xSegments X 轴向点分段数
         * @param ySegments Y 轴向点分段数
         * @returns 网格数据
         */
        static GenSphereData(size = 0.5, xSegments = 24, ySegments = 12) {
            let data = new FIREFLYX.MeshData();
            let poss = [];
            let uvs = [];
            let triindexs = [];
            let normals = [];
            xSegments = Math.max(3, Math.floor(xSegments));
            ySegments = Math.max(2, Math.floor(ySegments));
            let ix, iy;
            let index = 0;
            let grid = [];
            // generate vertices, normals and uvs
            for (iy = 0; iy <= ySegments; iy++) {
                let verticesRow = [];
                let v = iy / ySegments;
                for (ix = 0; ix <= xSegments; ix++) {
                    let u = ix / xSegments;
                    // vertex
                    let x = -size * Math.cos(u * Math.PI * 2) * Math.sin(v * Math.PI);
                    let y = size * Math.cos(v * Math.PI);
                    let z = size * Math.sin(u * Math.PI * 2) * Math.sin(v * Math.PI);
                    poss.push(x, y, z);
                    let val = poss.length % 3;
                    if (val == 0) {
                        // normal
                        let nx = x;
                        let ny = y;
                        let nz = z;
                        let num = Math.sqrt(nx * nx + ny * ny + nz * nz);
                        if (num > Number.MIN_VALUE) {
                            nx = nx / num;
                            ny = ny / num;
                            nz = nz / num;
                        }
                        else {
                            nx = 0;
                            ny = 0;
                            nz = 0;
                        }
                        normals.push(nx, ny, nz);
                    }
                    // uv
                    uvs.push(1 - u, v);
                    verticesRow.push(index++);
                }
                grid.push(verticesRow);
            }
            // indices
            for (iy = 0; iy < ySegments; iy++) {
                for (ix = 0; ix < xSegments; ix++) {
                    let a = grid[iy][ix + 1];
                    let b = grid[iy][ix];
                    let c = grid[iy + 1][ix];
                    let d = grid[iy + 1][ix + 1];
                    if (iy !== 0)
                        triindexs.push(a, d, b);
                    if (iy !== ySegments - 1)
                        triindexs.push(b, d, c);
                }
            }
            //
            data.posArr = new Float32Array(poss);
            data.uvArr = new Float32Array(uvs);
            data.normalArr = new Float32Array(normals);
            data.triIndexArr = new Uint16Array(triindexs);
            return data;
        }
        /** 往quad数据填入三角索引 */
        static fillIndexQuad(indexs, startIdx) {
            startIdx /= 3;
            indexs.push(startIdx + 0, startIdx + 3, startIdx + 1, startIdx + 1, startIdx + 3, startIdx + 2);
        }
        /**
         * 设置单色纹理
         * @param tex 纹理
         * @param color 颜色
         */
        static SetTexOneColor(tex, color) {
            let size = 2;
            let _w = size * 4;
            let pixelData = new Uint8Array(size * _w);
            let r = Math.floor(color.r * 255);
            let g = Math.floor(color.g * 255);
            let b = Math.floor(color.b * 255);
            let a = Math.floor(color.a * 255);
            for (let i = 0, len = size * _w; i < len; i += 4) {
                pixelData[i] = r;
                pixelData[i + 1] = g;
                pixelData[i + 2] = b;
                pixelData[i + 3] = a;
            }
            //set img
            tex.setTextureData(pixelData, size, size);
        }
        /**
         * 设置黑白格子纹理
         * @param tex 纹理
         */
        static SetTexGrid(tex) {
            let size = 128;
            let w = size * 4;
            let pixelData = new Uint8Array(size * w);
            let halfW = w / 2;
            let halfH = size / 2;
            let one = 255;
            let zero = 0;
            for (let y = 0; y < size; y++) {
                let numY = y * w;
                let _yIsSmall = (y < halfH);
                for (let x = 0; x < w; x += 4) {
                    let c = _yIsSmall == (x < halfW) ? one : zero;
                    pixelData[numY + x] = c;
                    pixelData[numY + x + 1] = c;
                    pixelData[numY + x + 2] = c;
                    pixelData[numY + x + 3] = one;
                }
            }
            //set img
            tex.setTextureData(pixelData, size, size);
        }
    }
    DefaultAsset.defMesh = {};
    DefaultAsset.defTexture = {};
    DefaultAsset.defGlslLib = {};
    DefaultAsset._glslLibCodeMap = {};
    DefaultAsset.defShader = {};
    DefaultAsset._shaderGenMap = {};
    DefaultAsset.defMaterial = {};
    FIREFLYX.DefaultAsset = DefaultAsset;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 文件加载
     */
    class FileLoad {
        static xhr(url, rType) {
            return new Promise((res, rej) => {
                let xhreq = new XMLHttpRequest();
                let success = false;
                xhreq.responseType = rType;
                xhreq.open("GET", url);
                xhreq.send();
                //event reg
                xhreq.onreadystatechange = () => {
                    if (xhreq.readyState == 4) {
                        switch (xhreq.status) {
                            case 200:
                                success = true;
                                res(xhreq.response);
                                break;
                            case 404:
                                rej(new Error(`got a 404: ${url}`));
                                break;
                            default:
                        }
                    }
                };
                xhreq.onloadend = () => {
                    if (success)
                        return;
                    if (!this.enableRetry) {
                        rej(new Error(`load fial: ${url}`));
                        return;
                    }
                    //加载失败 重试
                    console.log("加载失败 重试");
                };
                xhreq.onerror = (ev) => {
                    rej(new Error(`load error : ${url}`));
                };
            });
        }
        /**
         * 加载image 资源
         * @param uri 资源路径
         * @returns
         */
        static loadImage(uri) {
            let imgP = new Promise((res, rej) => {
                let img = new Image();
                img.src = uri;
                img.onload = () => { res(img); };
                img.onerror = (err) => { rej(err); };
            });
            return imgP;
        }
    }
    /** 网络加载失败后重试 */
    FileLoad.enableRetry = false;
    /** 加载失败后重试时间间隔 */
    FileLoad.retryInterval = 1000;
    FIREFLYX.FileLoad = FileLoad;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 着色器code依赖包含 类型 */
    let IncludeType;
    (function (IncludeType) {
        /** 引擎内建库 */
        IncludeType[IncludeType["LIB"] = 0] = "LIB";
        IncludeType[IncludeType["LINK"] = 1] = "LINK";
    })(IncludeType || (IncludeType = {}));
    /** glsl 片段资源
     *
     * 添加include 拓展
     * #pragma include "./xxx.glsl"
     * 参考 https://github1s.com/akira-cn/gl-renderer/blob/HEAD/src/renderer.js (line : 605)
    */
    class GLSL extends FIREFLYX.Asset {
        constructor() {
            super(...arguments);
            this.filePath = "";
            this._isLoadAll = false;
        }
        /** 是否依赖资源全部加载完毕 */
        isLoadedAll() {
            return this._isLoadAll;
        }
        /** 加载所有依赖的资源 */
        async loadAllFile() {
            if (this._isLoadAll)
                return;
            if (!this._code) {
                console.error(`can't to load ,_code is null.`);
                return;
            }
            const regMg = new RegExp(GLSL.IncludeLinkRegStr, `mg`);
            const matched = this._code.match(regMg);
            //
            if (!matched) {
                this._isLoadAll = true;
                return;
            }
            let _idx = this.filePath.lastIndexOf("/");
            let folderPath = `${this.filePath.substring(0, _idx)}/`;
            //遍历加载 include 片段
            let allFile = [];
            for (let i = 0, len = matched.length; i < len; i++) {
                const m = matched[i];
                const _matched = m.match(/(?:")(.*)(?:")/);
                if (!_matched)
                    continue;
                // const _type = _matched[0].indexOf("<") === 0 ? IncludeType.LIB : IncludeType.LINK;
                let content = _matched[1]; //内容主体
                //检查是否重复
                if (!this._includeMap[content]) {
                    this._includeMap[content] = [IncludeType.LINK, m];
                }
                else {
                    console.warn(`has repeat include of "${m}".`);
                }
                //加载
                //检查 指定content的资源已经被加载过了
                let fUrl = `${folderPath}${content}`;
                if (!FIREFLYX.AssetManager.GetAsset(fUrl)) {
                    allFile.push(FIREFLYX.AssetManager.LoadFile(fUrl));
                }
            }
            if (allFile.length <= 0) {
                this._isLoadAll = true;
                return;
            }
            //等待所有载完
            let glsls = await Promise.all(allFile)
                .catch((reason) => {
                console.error(`include fail , not loaded. \n reason : ${reason}`);
            });
            //load all
            let allLoad = [];
            if (glsls && glsls.length > 0) {
                for (let i = 0, len = glsls.length; i < len; i++) {
                    let _g = glsls[i];
                    if (_g.isLoadedAll())
                        continue;
                    allLoad.push(_g.loadAllFile());
                }
            }
            //等加载
            await Promise.all(allLoad);
            //标记 载完
            this._isLoadAll = true;
        }
        /**
         * 设置 glsl片段代码文本
         * @param code glsl片段代码文本
         */
        setCode(code) {
            if (!code)
                return;
            if (this._code) {
                //只让设置一次
                console.warn(`GLSL only can setCode once.`);
                return;
            }
            this._needFullParse = true;
            this._code = code;
            // const hasInc = new RegExp(GLSL.IncludeRegStr, `mg`).test(this._code);
            const regLib = new RegExp(GLSL.IncludeLibRegStr, `mg`);
            const hasLib = regLib.test(this._code);
            const hasLink = new RegExp(GLSL.IncludeLinkRegStr, `mg`).test(this._code);
            this._isLoadAll = !hasLink;
            this._includeMap = hasLib || hasLink ? {} : null;
            //填入 lib
            if (hasLib) {
                const matched = this._code.match(regLib);
                for (let i = 0, len = matched.length; i < len; i++) {
                    const m = matched[i];
                    const _matched = m.match(/(?:<)(.*)(?:>)/);
                    if (!_matched)
                        continue;
                    let content = _matched[1]; //内容主体
                    //检查是否重复
                    if (!this._includeMap[content]) {
                        this._includeMap[content] = [IncludeType.LIB, m];
                    }
                    else {
                        console.warn(`has repeat include of "${m}".`);
                    }
                }
            }
            // console.log(`code:${this._code}`);
        }
        /** 获取完整的glsl代码文本(合并过依赖库的) */
        getFull() {
            var _a;
            if (!this._code) {
                console.error(`code is empty , need call "setCode" to set code.`);
                return null;
            }
            if (!this._isLoadAll) {
                console.error(`have include dependent not load , need call "loadAllFile".`);
                return null;
            }
            if (!this._includeMap || !this._needFullParse)
                return this._code; //没有依赖
            //组合依赖
            let _fullCode = this._code;
            let _idx = this.filePath.lastIndexOf("/");
            let folderPath = `${this.filePath.substring(0, _idx)}/`;
            const keys = Object.keys(this._includeMap);
            for (let i = 0, len = keys.length; i < len; i++) {
                const k = keys[i];
                if (!this._includeMap[k]) {
                    continue;
                }
                const inc = this._includeMap[k];
                let _glsl = null;
                let msg = "";
                switch (inc[0]) {
                    case IncludeType.LIB:
                        _glsl = FIREFLYX.DefaultAsset.getGLSLLib(k);
                        if (!_glsl) {
                            msg = `glsl lib <${k}> not find.`;
                        }
                        break;
                    case IncludeType.LINK:
                        let fUrl = `${folderPath}${k}`;
                        _glsl = FIREFLYX.AssetManager.GetAsset(fUrl);
                        if (!_glsl) {
                            msg = `file "${fUrl}" not find.`;
                        }
                        break;
                    default:
                }
                let _incStr = "";
                if (_glsl) {
                    _incStr = (_a = _glsl.getFull()) !== null && _a !== void 0 ? _a : _incStr;
                }
                else {
                    console.warn(msg);
                }
                //替换掉 include 字符串
                _fullCode = _fullCode.replace(inc[1], _incStr);
            }
            this._needFullParse = false; //parse 完了不再需要Parse
            this._code = _fullCode;
            return _fullCode;
        }
    }
    // private static readonly IncludeRegStr = `^ *#pragma *include\\s+.*`;
    GLSL.IncludeLibRegStr = `^ *#pragma +include +<.*>`;
    GLSL.IncludeLinkRegStr = `^ *#pragma +include +".*"`;
    FIREFLYX.GLSL = GLSL;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./asset.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** accessor 的 ComponentType*/
    let AccComponentTEnum;
    (function (AccComponentTEnum) {
        AccComponentTEnum[AccComponentTEnum["BYTE"] = 5120] = "BYTE";
        AccComponentTEnum[AccComponentTEnum["UNSIGNED_BYTE"] = 5121] = "UNSIGNED_BYTE";
        AccComponentTEnum[AccComponentTEnum["SHORT"] = 5122] = "SHORT";
        AccComponentTEnum[AccComponentTEnum["UNSIGNED_SHORT"] = 5123] = "UNSIGNED_SHORT";
        AccComponentTEnum[AccComponentTEnum["UNSIGNED_INT"] = 5125] = "UNSIGNED_INT";
        AccComponentTEnum[AccComponentTEnum["FLOAT"] = 5126] = "FLOAT";
    })(AccComponentTEnum || (AccComponentTEnum = {}));
    /** MeshPrimitives 模式  */
    let MeshPrimitivesMode;
    (function (MeshPrimitivesMode) {
        MeshPrimitivesMode[MeshPrimitivesMode["POINTS"] = 0] = "POINTS";
        MeshPrimitivesMode[MeshPrimitivesMode["LINES"] = 1] = "LINES";
        MeshPrimitivesMode[MeshPrimitivesMode["LINE_LOOP"] = 2] = "LINE_LOOP";
        MeshPrimitivesMode[MeshPrimitivesMode["LINE_STRIP"] = 3] = "LINE_STRIP";
        MeshPrimitivesMode[MeshPrimitivesMode["TRIANGLES"] = 4] = "TRIANGLES";
        MeshPrimitivesMode[MeshPrimitivesMode["TRIANGLE_STRIP"] = 5] = "TRIANGLE_STRIP";
        MeshPrimitivesMode[MeshPrimitivesMode["TRIANGLE_FAN"] = 6] = "TRIANGLE_FAN";
    })(MeshPrimitivesMode || (MeshPrimitivesMode = {}));
    /** GLTF 资源 */
    class GLTF extends FIREFLYX.Asset {
        constructor() {
            super(...arguments);
            this._isLoadAll = false;
            this._isParseAll = false;
            this._gltfPath = "";
            this._accInfoCacheMap = {};
            this._binResKeys = [];
            this._meshResKeys = [];
            this._imageResKeys = [];
            this._materialResKeys = [];
            this._accIdMarkMap = new Map();
        }
        /** 设置数据 */
        setData(data, gltfPath) {
            this._data = data;
            this._gltfPath = gltfPath;
            // tslint:disable-next-line: no-unused-expression
            this._isLoadAll = false;
            this._isParseAll = false;
        }
        /** 是否依赖资源全部加载完毕 */
        isLoadedAll() {
            return this._isLoadAll;
        }
        /** 是否依赖资源全部解析完毕 */
        isParseAll() {
            return this._isParseAll;
        }
        /** 加载所有依赖的资源 */
        async loadAllFile() {
            if (this._isLoadAll)
                return;
            if (!this._data) {
                console.error(`can't to load ,_data is null.`);
                return;
            }
            //加载所有文件
            const binFiles = [];
            //二进制数据
            const buffers = this._data.buffers;
            if (buffers) {
                buffers.forEach((buf, idx) => {
                    let bufPath = buf.uri ? buf.uri : `${GLTF.BIN_TAG}${idx}`;
                    const fUrl = `${this._gltfPath}${bufPath}`;
                    this._binResKeys.push(fUrl);
                    if (buf.uri) { //有 uri 需要加载 或 base64解析？
                        if (!FIREFLYX.AssetManager.GetAsset(fUrl)) {
                            binFiles.push(FIREFLYX.AssetManager.LoadFile(fUrl));
                        }
                    }
                });
            }
            //等待加载 buffer bin 文件
            if (binFiles.length > 0) {
                await Promise.all(binFiles);
            }
            //图
            const images = this._data.images;
            if (images) {
                const imgFile = [];
                images.forEach((i, index) => {
                    var _a;
                    let resPath = "";
                    if (i.uri) {
                        resPath = `${this._gltfPath}${i.uri}`;
                        if (!FIREFLYX.AssetManager.GetAsset(resPath)) {
                            imgFile.push(FIREFLYX.AssetManager.LoadFile(resPath));
                        }
                        FIREFLYX.ResPathProcessorParameterMap.set(resPath, { flipY: false }); //import texture时不要 Y 翻转
                        //uri 是否是base64 信息
                    }
                    else if (i.bufferView != null) {
                        let imgName = (_a = i.name) !== null && _a !== void 0 ? _a : `${GLTF.IMAGE_TAG}${index}`;
                        resPath = `${this._gltfPath}${imgName}`;
                        imgFile.push(this.getImageByBin(index));
                    }
                    this._imageResKeys.push(resPath);
                });
                if (imgFile.length > 0) {
                    await Promise.all(imgFile);
                }
            }
            //等待所有载完
            this._isLoadAll = true;
        }
        /** 解析所有资源，纹理、材质、网格、动画... */
        async parseAllRes() {
            if (this._isParseAll)
                return;
            //当前对象不持有具体的资源对象，只持有key ，具体对象方到 AssetManager.CacheResMap中
            const _data = this._data;
            //解析纹理
            if (_data.textures) {
                for (let i = 0, len = _data.textures.length; i < len; i++) {
                    const tex = this.getTexture(i);
                    if (!tex)
                        continue;
                }
            }
            //解析材质
            for (let i = 0, len = _data.materials.length; i < len; i++) {
                const material = this.getMaterial(i);
                if (!material)
                    continue;
                const rKey = `${this._gltfPath}${GLTF.MATERIAL_TAG}${material === null || material === void 0 ? void 0 : material.name}`;
                this._materialResKeys.push(rKey);
                FIREFLYX.AssetManager.SetAsset(rKey, material);
            }
            //解析网格
            for (let i = 0, len = _data.meshes.length; i < len; i++) {
                const rM = this.getMesh(i);
                if (!rM)
                    continue;
                const rKey = `${this._gltfPath}${GLTF.MESH_TAG}${rM === null || rM === void 0 ? void 0 : rM.name}`;
                this._meshResKeys.push(rKey);
                FIREFLYX.AssetManager.SetAsset(rKey, rM);
            }
            //解析动画
            //等待所有载完
            this._isParseAll = true;
        }
        /** 获取节点对象 */
        getGameObject() {
            const d = this._data;
            const s = d.scenes[d.scene];
            const nodes = s.nodes;
            if (!nodes)
                return null;
            //模型跟节点
            const gltfGameObject = new FIREFLYX.GameObject(s.name);
            const nodeRoot = gltfGameObject.transform;
            //子节点
            for (let i = 0, len = nodes.length; i < len; i++) {
                const nIdx = nodes[i];
                const n = this.getNode(nIdx);
                if (n) {
                    n.transform.parent = nodeRoot;
                }
            }
            return gltfGameObject;
        }
        /** 获取节点对象 （异步） */
        async getGameObjectAsync() {
            if (!this._isLoadAll) {
                await this.loadAllFile();
            }
            if (!this._isParseAll) {
                await this.parseAllRes();
            }
            return this.getGameObject();
        }
        /**
         * 获取指定索引的节点对象
         * @param index 索引值
         * @returns
         */
        getNode(index) {
            const node = this._data.nodes[index];
            const n = new FIREFLYX.GameObject(node.name);
            const trans = n.transform;
            //设置transform
            const matrix = node.matrix;
            if (!matrix) {
                //位置、旋转、缩放 数据
                const translation = node.translation;
                if (translation) {
                    //修正 x ,左右手坐标系不一致问题 
                    trans.localPostion.set(-1 * translation[0], translation[1], translation[2]);
                    trans.localPostion = trans.localPostion;
                }
                const rotation = node.rotation;
                if (rotation) {
                    trans.localRotation.set(rotation[0], rotation[1], rotation[2], rotation[3]);
                    trans.localRotation = trans.localRotation;
                }
                const scale = node.scale;
                if (scale) {
                    trans.localScale.set(scale[0], scale[1], scale[2]);
                    trans.localScale = trans.localScale;
                }
            }
            else {
                //矩阵数据
                const lMtx = trans.getLocalMatrix();
                for (let i = 0; i < 16; i++) {
                    lMtx.m[i] = matrix[i];
                }
                lMtx.m[12] *= -1; //修正 x ,左右手坐标系不一致问题 
                lMtx.decompose(trans.localScale, trans.localRotation, trans.localPostion);
                trans.localPostion = trans.localPostion;
                trans.localRotation = trans.localRotation;
                trans.localScale = trans.localScale;
            }
            //静态网格
            const meshIdx = node.mesh;
            let m = null;
            if (meshIdx != null) {
                const rKey = this._meshResKeys[meshIdx];
                m = FIREFLYX.AssetManager.GetAsset(rKey);
            }
            //蒙皮骨骼
            const isSkinMesh = false;
            //相机
            //组件拼装
            if (m) {
                const mesh = this._data.meshes[meshIdx];
                if (!isSkinMesh) {
                    const mr = n.addComponent(FIREFLYX.MeshRenderer);
                    mr.mesh = m;
                    //添加材质
                    const primitves = mesh.primitives;
                    if (primitves) {
                        //每个primitves 对应一组材质
                        for (let i = 0, len = primitves.length; i < len; i++) {
                            const primitve = primitves[i];
                            if (primitve.material == null)
                                continue;
                            // const material = this._data.materials[primitve.material];
                            //加材质
                            // const mat = new Material(`DEF_MODEL`);
                            const resMatKey = this._materialResKeys[primitve.material];
                            const mat = FIREFLYX.AssetManager.GetAsset(resMatKey);
                            if (!mat)
                                continue;
                            mr.materialGroupList[0][i] = mat;
                        }
                    }
                }
            }
            //子节点
            const children = node.children;
            if (children) {
                for (let i = 0, len = children.length; i < len; i++) {
                    const cN = this.getNode(children[i]);
                    if (!cN)
                        continue;
                    cN.transform.parent = trans;
                }
            }
            return n;
        }
        /**
         * 获取 image 通过 二进制数据
         * @param imageIdx image 索引
         * @returns
         */
        async getImageByBin(imageIdx) {
            var _a, _b;
            let image = this._data.images[imageIdx];
            if (image.bufferView == null)
                return;
            let bufView = this._data.bufferViews[image.bufferView];
            let binResKey = this._binResKeys[bufView.buffer];
            let bin = FIREFLYX.AssetManager.GetAsset(binResKey);
            if (!bin)
                return;
            let imgBin = new Uint8Array(bin.buffer, (_a = bufView.byteOffset) !== null && _a !== void 0 ? _a : 0, bufView.byteLength);
            const blob = new Blob([imgBin], {
                type: image.mimeType
            });
            let sourceURI = URL.createObjectURL(blob);
            let img = await FIREFLYX.FileLoad.loadImage(sourceURI);
            //纹理储存设置
            let imgName = (_b = image.name) !== null && _b !== void 0 ? _b : `${GLTF.IMAGE_TAG}${imageIdx}`;
            let tex = new FIREFLYX.Texture2D();
            tex.paramSetting.flipY = false;
            tex.setTextureData(img);
            //应用参数
            tex.apply();
            //保存到 AssetManager
            let resPath = `${this._gltfPath}${imgName}`;
            FIREFLYX.AssetManager.SetAsset(resPath, tex);
        }
        /**
         * 获取指定索引的纹理对象
         * @param index 索引值
         * @returns
         */
        getTexture(index) {
            var _a, _b, _c, _d;
            const d = this._data;
            const texture = d.textures[index];
            const gl = FIREFLYX.App.webgl2;
            const resKeyImage = this._imageResKeys[texture.source];
            const resTexture = FIREFLYX.AssetManager.GetAsset(resKeyImage);
            if (!resTexture)
                return null;
            //sampler
            let magFilter = gl.LINEAR;
            let minFilter = gl.LINEAR;
            let wrapS = gl.REPEAT;
            let wrapT = gl.REPEAT;
            if (texture.sampler != null && d.samplers) {
                const sampler = d.samplers[texture.sampler];
                magFilter = (_a = sampler.magFilter) !== null && _a !== void 0 ? _a : magFilter;
                minFilter = (_b = sampler.minFilter) !== null && _b !== void 0 ? _b : minFilter;
                wrapS = (_c = sampler.wrapS) !== null && _c !== void 0 ? _c : wrapS;
                wrapT = (_d = sampler.wrapT) !== null && _d !== void 0 ? _d : wrapT;
            }
            //设置参数
            const para = resTexture.paramSetting;
            para.linear = magFilter == gl.LINEAR || (minFilter == gl.LINEAR_MIPMAP_LINEAR || minFilter == gl.LINEAR_MIPMAP_NEAREST || minFilter == gl.LINEAR) ? true : false;
            para.mipmap = minFilter == gl.LINEAR_MIPMAP_LINEAR ||
                minFilter == gl.LINEAR_MIPMAP_NEAREST ||
                minFilter == gl.NEAREST_MIPMAP_LINEAR ||
                minFilter == gl.NEAREST_MIPMAP_NEAREST
                ? true : false;
            para.warpU = wrapS;
            para.warpV = wrapT;
            //应用纹理参数
            resTexture.apply();
            return null;
        }
        /**
         * 获取指定索引的渲染材质对象
         * @param index
         * @returns
         */
        getMaterial(index) {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
            const d = this._data;
            const material = d.materials[index];
            const mName = (_a = material.name) !== null && _a !== void 0 ? _a : `Material_${index}`;
            if (!material)
                return null;
            const resMaterial = new FIREFLYX.Material(mName);
            //doubleSided
            resMaterial.rasterState = FIREFLYX.RasterState.getState(material.doubleSided ? FIREFLYX.CullMode.NONE : FIREFLYX.CullMode.BACK);
            //暂时 用普通shader 
            // resMaterial.shader = DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR);
            resMaterial.shader = FIREFLYX.DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_STANDARD);
            const pbrMetallicRoughness = material.pbrMetallicRoughness;
            //PBR 金属度 + 粗糙度
            if (pbrMetallicRoughness) {
                //主纹理
                const mainTexRKey = this._imageResKeys[(_c = (_b = pbrMetallicRoughness.baseColorTexture) === null || _b === void 0 ? void 0 : _b.index) !== null && _c !== void 0 ? _c : -1];
                const resTex = FIREFLYX.AssetManager.GetAsset(mainTexRKey);
                resMaterial.setProperty(`u_mainTex`, resTex);
                //金属粗糙纹理
                const metRouRKey = this._imageResKeys[(_e = (_d = pbrMetallicRoughness.metallicRoughnessTexture) === null || _d === void 0 ? void 0 : _d.index) !== null && _e !== void 0 ? _e : -1];
                const resMRTex = FIREFLYX.AssetManager.GetAsset(metRouRKey);
                resMaterial.setProperty(`u_metaRougTex`, resMRTex);
            }
            //alphaMode 半透模式
            if (material.alphaMode) {
                //默认 ： "OPAQUE"
            }
            //alphaCutoff alpha剔除
            if (material.alphaCutoff) {
                //默认 ： 0.5
            }
            //双面绘制
            if (material.doubleSided) {
                //默认 ： false
            }
            //法线纹理
            if (material.normalTexture) {
                const normalTexRKey = this._imageResKeys[(_g = (_f = material.normalTexture) === null || _f === void 0 ? void 0 : _f.index) !== null && _g !== void 0 ? _g : -1];
                const resNTex = FIREFLYX.AssetManager.GetAsset(normalTexRKey);
                resMaterial.setProperty(`u_normalTex`, resNTex);
                //法线纹理缩放
                const nScale = (_j = (_h = material.normalTexture) === null || _h === void 0 ? void 0 : _h.scale) !== null && _j !== void 0 ? _j : 1;
                resMaterial.setProperty(`u_normalScale`, nScale);
            }
            //自发光纹理
            if (material.emissiveTexture) {
                const emissiveTexRKey = this._imageResKeys[(_l = (_k = material.emissiveTexture) === null || _k === void 0 ? void 0 : _k.index) !== null && _l !== void 0 ? _l : -1];
                const resETex = FIREFLYX.AssetManager.GetAsset(emissiveTexRKey);
                resMaterial.setProperty(`u_emissiveTex`, resETex);
            }
            //遮蔽纹理
            if (material.occlusionTexture) {
                const occlusionTexRKey = this._imageResKeys[(_o = (_m = material.occlusionTexture) === null || _m === void 0 ? void 0 : _m.index) !== null && _o !== void 0 ? _o : -1];
                const resOTex = FIREFLYX.AssetManager.GetAsset(occlusionTexRKey);
                resMaterial.setProperty(`u_occlusionTex`, resOTex);
                //遮蔽 强度
                const oStrength = (_q = (_p = material.occlusionTexture) === null || _p === void 0 ? void 0 : _p.strength) !== null && _q !== void 0 ? _q : 1;
                resMaterial.setProperty(`u_emissiveStrength`, oStrength);
            }
            //自发光 颜色系数
            if (material.emissiveFactor) {
                resMaterial.setProperty(`u_emissiveFactor`, material.emissiveFactor);
            }
            return resMaterial;
        }
        /**
         * x 翻转
         * 处理左右手坐标系不一致问题
         * @param accIdx
         * @param arr
         */
        reverseVec3X(accIdx, arr) {
            if (!this._accIdMarkMap.get(accIdx)) {
                this._accIdMarkMap.set(accIdx, true);
                //修正坐标系
                for (let k = 0, len2 = arr.length; k < len2; k += 3) {
                    arr[k] *= -1; //x 翻转 处理左右手坐标系不一致问题
                }
            }
        }
        // private reverseVec3All(accIdx: number, arr: AccComponentType) {
        //     if (!this._accIdMarkMap.get(accIdx)) {
        //         this._accIdMarkMap.set(accIdx, true);
        //         //修正坐标系
        //         for (let k = 0, len2 = arr.length; k < len2; k += 3) {
        //             //x 翻转 处理左右手坐标系不一致问题
        //             arr[k] *= -1;
        //             arr[k + 1] *= -1;
        //             arr[k + 2] *= -1;
        //         }
        //     }
        // }
        /**
         * 顶点变形处理
         * @param morphWeights
         * @param morphTargets
         * @param aKey
         * @param arr
         * @param reverseVec3X
         * @returns
         */
        morphVertex(morphWeights, morphTargets, aKey, arr, reverseVec3X = false) {
            if (!morphWeights || !morphTargets)
                return false;
            //顶点变形操作
            for (let k = 0, len2 = morphTargets.length; k < len2; k++) {
                const morphTarget = morphTargets[k];
                const morphWeight = morphWeights[k];
                const _accIdx = morphTarget[aKey];
                if (morphWeight == null || _accIdx == null)
                    break;
                const _arr = this.getTypeArrByAcc(_accIdx);
                if (reverseVec3X) {
                    this.reverseVec3X(_accIdx, _arr);
                }
                //变形融合
                for (let l = 0, len3 = _arr.length; l < len3; l++) {
                    arr[l] += _arr[l] * morphWeight;
                }
            }
            return true;
        }
        /**
         * 获取指定索引的静态网格节点
         * @param index 索引值
         */
        getMesh(index) {
            var _a, _b, _c, _d, _e, _f;
            const d = this._data;
            const mesh = d.meshes[index];
            const name = (_a = mesh.name) !== null && _a !== void 0 ? _a : `gltf_mesh_${index}`;
            const resMesh = new FIREFLYX.Mesh();
            resMesh.name = name;
            const mData = resMesh.data = new FIREFLYX.MeshData();
            //解析
            const primArr = mesh.primitives;
            const morphWeights = mesh.weights;
            //遍历 primitive
            for (let i = 0, len = primArr.length; i < len; i++) {
                const p = primArr[i];
                const mP = new FIREFLYX.MeshPrimitive();
                // mP.frontFace = App.webgl2.CW;
                const attr = p.attributes;
                const keys = Object.keys(attr);
                const lastVertexCount = mData.posArr ? mData.posArr.length / 3 : 0;
                const morphTargets = p.targets;
                let hasPosMorphed = false;
                for (let j = 0, len1 = keys.length; j < len1; j++) {
                    const aKey = keys[j];
                    const attrLayout = FIREFLYX.AttributeLayout[aKey];
                    if (attrLayout == null)
                        continue;
                    //accessor
                    const accIdx = attr[aKey];
                    const acc = d.accessors[accIdx];
                    const arr = this.getTypeArrByAcc(accIdx);
                    const isPostion = aKey == "POSITION";
                    //翻转x
                    if (isPostion) {
                        this.reverseVec3X(accIdx, arr);
                    }
                    //变形处理
                    const hasMorphed = this.morphVertex(morphWeights, morphTargets, aKey, arr, isPostion);
                    if (isPostion) {
                        hasPosMorphed = hasMorphed;
                    }
                    switch (attrLayout) {
                        case FIREFLYX.AttributeLayout.POSITION:
                            mP.start = (_c = (_b = mData.posArr) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
                            mP.length = arr.length;
                            mData.posArr = this.tryMergeTypeArr(mData.posArr, arr, Float32Array);
                            break;
                        case FIREFLYX.AttributeLayout.TEXCOORD_0:
                            mData.uvArr = this.tryMergeTypeArr(mData.uvArr, arr, Float32Array);
                            break;
                        case FIREFLYX.AttributeLayout.TEXCOORD_1:
                            mData.uvArr1 = this.tryMergeTypeArr(mData.uvArr1, arr, Float32Array);
                            break;
                        case FIREFLYX.AttributeLayout.TEXCOORD_2:
                            mData.uvArr2 = this.tryMergeTypeArr(mData.uvArr2, arr, Float32Array);
                            break;
                        case FIREFLYX.AttributeLayout.NORMAL:
                            // this.reverseVec3All(accIdx, arr);
                            mData.normalArr = this.tryMergeTypeArr(mData.normalArr, arr, Float32Array);
                            break;
                        case FIREFLYX.AttributeLayout.TANGENT:
                            mData.tangentArr = this.tryMergeTypeArr(mData.tangentArr, arr, Float32Array);
                            break;
                        // case AttributeLayout.COLOR_0: mData.colorArr = new Float32Array(bin.buffer, offset, byteLen); break;
                        // case AttributeLayout.TEXCOORD_1: mData.uv1Arr = new Float32Array(bin.buffer, offset, byteLen); break;
                        // case AttributeLayout.TANGENT: mData.tangentArr = new Float32Array(bin.buffer, offset, byteLen); break;
                        default:
                    }
                    // bufferView.target ，不用处理,mData.triIndexArr 中有数据就走 ELEMENT_ARRAY_BUFFER
                    //accessor.max | min
                    if (attrLayout == FIREFLYX.AttributeLayout.POSITION) {
                        if (acc.min && acc.max) {
                            mData.posMin.x = Math.min(i == 0 ? Number.POSITIVE_INFINITY : mData.posMin.x, acc.min[0]);
                            mData.posMin.y = Math.min(i == 0 ? Number.POSITIVE_INFINITY : mData.posMin.y, acc.min[1]);
                            mData.posMin.z = Math.min(i == 0 ? Number.POSITIVE_INFINITY : mData.posMin.z, acc.min[2]);
                            mData.posMax.x = Math.max(i == 0 ? Number.NEGATIVE_INFINITY : mData.posMax.x, acc.max[0]);
                            mData.posMax.y = Math.max(i == 0 ? Number.NEGATIVE_INFINITY : mData.posMax.y, acc.max[1]);
                            mData.posMax.z = Math.max(i == 0 ? Number.NEGATIVE_INFINITY : mData.posMax.z, acc.max[2]);
                        }
                    }
                }
                if (hasPosMorphed) {
                    //坐标有变形需要修复normal
                    //....
                }
                //primitives.indices
                if (p.indices != null) {
                    const arr = this.getTypeArrByAcc(p.indices);
                    //三角形顶点索引
                    const needTriIdxOffset = i != 0;
                    if (needTriIdxOffset) {
                        for (let j = 0, len1 = arr.length; j < len1; j++) {
                            arr[j] += lastVertexCount;
                        }
                    }
                    mP.start = (_e = (_d = mData.triIndexArr) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0;
                    mP.length = arr.length;
                    const burClass = arr.constructor;
                    mData.triIndexArr = this.tryMergeTypeArr(mData.triIndexArr, arr, burClass);
                    //buffer Start
                    const triIdxBufByteSize = this.getAccComponentSize(this._accInfoCacheMap[p.indices].compType);
                    mP.bufferStart = mP.start * triIdxBufByteSize;
                }
                else {
                    //不走 顶点索引模式
                    mP.start /= 3;
                    mP.length /= 3;
                    mP.bufferStart = mP.start * 4;
                }
                //primitives.material
                mP.material = i;
                //primitives.mode
                mP.mode = this.getMeshPGLMode((_f = p.mode) !== null && _f !== void 0 ? _f : MeshPrimitivesMode.TRIANGLES);
                // mP.mode = this.getMeshPGLMode(p.mode ?? MeshPrimitivesMode.POINTS);
                //
                resMesh.primitives.push(mP);
            }
            //将来支持动画时处理 weights
            //applay
            // resMesh.data.calcPosBoundary();  //gltf 有带 min max 包围盒 ，不需要算了
            resMesh.apply();
            return resMesh;
        }
        /**
         * 获取 MeshPrimitivesMode 对应webgl mode常量
         * @param mode MeshPrimitivesMode
         * @returns webgl mode常量
         */
        getMeshPGLMode(mode) {
            const gl = FIREFLYX.App.webgl2;
            const glKey = MeshPrimitivesMode[mode];
            return gl[glKey];
        }
        /**
         * 尝试获取 合并 两个类型化数组
         * @param arrA 类型化数组对象A
         * @param arrB 类型化数组对象A
         * @param c 类型化数组构造器
         * @returns 合并后的类型化数组对象
         */
        tryMergeTypeArr(arrA, arrB, c) {
            if (!arrA && !arrB)
                return null;
            if (!arrA)
                return arrB;
            if (!arrB)
                return arrA;
            let result;
            //检查是否是连续的
            if (arrA.buffer == arrB.buffer && arrA.byteOffset + arrA.byteLength == arrB.byteOffset) {
                //是连续不用新开内存,只要 拓展一下范围
                result = new c(arrA.buffer, arrA.byteOffset, arrA.length + arrB.length);
            }
            else {
                //不是连续的,需要新开内存，然后将两 类型化数组，拷贝放入
                result = new c(arrA.length + arrB.length);
                result.set(arrA);
                result.set(arrB, arrA.length);
            }
            return result;
        }
        /**
         * 通过Acc 获取类型数组对象
         * @param accIndex Acc 索引
         * @returns 型数组对象
         */
        getTypeArrByAcc(accIndex) {
            var _a, _b, _c;
            const d = this._data;
            //获取bin 文件url
            let accInfo = this._accInfoCacheMap[accIndex];
            if (!accInfo) {
                //acc
                const acc = d.accessors[accIndex];
                //bufView
                const bufView = d.bufferViews[(_a = acc.bufferView) !== null && _a !== void 0 ? _a : 0];
                const bufferIdx = bufView.buffer;
                //buffer
                // const buffer = d.buffers[bufView.buffer];
                // const fUrl = `${this._gltfPath}${buffer.uri}`;
                const eleSize = this.getAccTypeEleSize(acc.type);
                const unitByteSize = this.getAccComponentSize(acc.componentType) * eleSize;
                const isRealStrideMode = bufView.byteStride != null && bufView.byteStride > unitByteSize;
                const compType = acc.componentType;
                let byteOffset = 0;
                let byteLen = 0;
                if (!isRealStrideMode) {
                    //不用考虑 bufferView.byteStride 交错数据
                    byteOffset = ((_b = acc.byteOffset) !== null && _b !== void 0 ? _b : 0) + ((_c = bufView.byteOffset) !== null && _c !== void 0 ? _c : 0);
                    byteLen = acc.count * eleSize;
                }
                else {
                    //
                }
                this._accInfoCacheMap[accIndex] = accInfo = { bufferIdx, compType, byteOffset, byteLen };
            }
            //bin 获取
            const resKey = this._binResKeys[accInfo.bufferIdx];
            const bin = FIREFLYX.AssetManager.GetAsset(resKey);
            const typeArrConstructor = this.getAccComponentArrTypeC(accInfo.compType);
            //生成数组
            const arr = new typeArrConstructor(bin.buffer, accInfo.byteOffset, accInfo.byteLen);
            return arr;
        }
        /**
         * 获取 AccType 对应元素数量
         * @param type AccType
         * @returns 元素数量
         */
        getAccTypeEleSize(type) {
            switch (type) {
                case "SCALAR": return 1;
                case "VEC2": return 2;
                case "VEC3": return 3;
                case "VEC4": return 4;
                case "MAT2": return 4;
                case "MAT3": return 9;
                case "MAT4": return 16;
                default: return 0;
            }
        }
        /**
         * 获取 AccComponent 对应类型化数组构造器
         * @param componentType AccComponent
         * @returns 类型化数组 type
         */
        getAccComponentArrTypeC(componentType) {
            switch (componentType) {
                case AccComponentTEnum.BYTE: return Int8Array;
                case AccComponentTEnum.UNSIGNED_BYTE: return Uint8Array;
                case AccComponentTEnum.SHORT: return Int16Array;
                case AccComponentTEnum.UNSIGNED_SHORT: return Uint16Array;
                case AccComponentTEnum.UNSIGNED_INT: return Uint32Array;
                case AccComponentTEnum.FLOAT: return Float32Array;
                default: return Float32Array;
            }
        }
        /**
         * 获取 AccComponent 对应字节长度
         * @param componentType AccComponent
         * @returns 字节长度
         */
        getAccComponentSize(componentType) {
            switch (componentType) {
                case AccComponentTEnum.BYTE: return 1;
                case AccComponentTEnum.UNSIGNED_BYTE: return 1;
                case AccComponentTEnum.SHORT: return 2;
                case AccComponentTEnum.UNSIGNED_SHORT: return 2;
                case AccComponentTEnum.UNSIGNED_INT: return 4;
                case AccComponentTEnum.FLOAT: return 4;
                default: return 0;
            }
        }
    }
    GLTF.BIN_TAG = "_GLTF_BIN_";
    GLTF.MESH_TAG = "_GLTF_MESH_";
    GLTF.IMAGE_TAG = "_GLTF_IMAGE_";
    GLTF.MATERIAL_TAG = "_GLTF_MATERIAL_";
    FIREFLYX.GLTF = GLTF;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./asset.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 原始 HTMLImageElement 的持有 资源
     */
    class HtmlImage extends FIREFLYX.Asset {
    }
    FIREFLYX.HtmlImage = HtmlImage;
})(FIREFLYX || (FIREFLYX = {}));
//cullingMode
//POLYGON_OFFSET_FILL, POLYGON_OFFSET_FACTOR POLYGON_OFFSET_UNITS
var FIREFLYX;
(function (FIREFLYX) {
    /** 渲染面剔除模式 */
    let CullMode;
    (function (CullMode) {
        /** 不剔除 */
        CullMode[CullMode["NONE"] = 0] = "NONE";
        /** 剔除背面 */
        CullMode[CullMode["BACK"] = 1] = "BACK";
        /** 剔除前面 */
        CullMode[CullMode["FRONT"] = 2] = "FRONT";
    })(CullMode = FIREFLYX.CullMode || (FIREFLYX.CullMode = {}));
    /** 光栅状态 */
    class RasterState {
        // eslint-disable-next-line no-useless-constructor
        constructor() {
            //
        }
        /** 面剔除模式 */
        get cullMode() { return this.states[0]; }
        /** 多边形偏移因子值 (默认0) */
        get polygonOffsetFactor() { return this.states[1]; }
        /** 多边形偏移单元值 (默认0)*/
        get polygonOffsetUnits() { return this.states[2]; }
        /**
         * 获取光栅状态
         * @param cullMode 面剔除模式
         * @param polygonOffsetFactor 多边形偏移因子值
         * @param polygonOffsetUnits 多边形偏移单元值
         * @returns 光栅状态对象
         */
        static getState(cullMode = CullMode.BACK, polygonOffsetFactor = 0, polygonOffsetUnits = 0) {
            let stateKey = `${cullMode}_${polygonOffsetFactor}_${polygonOffsetUnits}`;
            if (isNaN(polygonOffsetFactor))
                polygonOffsetFactor = 0;
            if (isNaN(polygonOffsetUnits))
                polygonOffsetUnits = 0;
            let enablePlgOffset = polygonOffsetFactor == 0 && polygonOffsetUnits == 0;
            if (enablePlgOffset) {
                //限定小数位
                polygonOffsetFactor = Number(polygonOffsetFactor.toFixed(1));
                polygonOffsetUnits = Number(polygonOffsetUnits.toFixed(1));
            }
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new RasterState();
                state.states = [cullMode, polygonOffsetFactor, polygonOffsetUnits];
            }
            return state;
        }
    }
    RasterState.cacheStateMap = {};
    FIREFLYX.RasterState = RasterState;
})(FIREFLYX || (FIREFLYX = {}));
//compareFunction (ZTest)
//writeEnabled  (ZWrite)
var FIREFLYX;
(function (FIREFLYX) {
    /** 深度状态对象 */
    class DepthState {
        // eslint-disable-next-line no-useless-constructor
        constructor() {
            //
        }
        /** 深度比较函数 , 值为 null 关闭*/
        get zTestCompareFunc() { return this.states[0] == 0 ? null : this.states[0]; }
        /** 是否深度写入 */
        get zWrite() { return this.states[1] == 1; }
        /**
         * 获取深度状态
         * @param zWrite 是否启用深度写入
         * @param zTestCFunc 深度测试比较函数(关闭给值 null)
         * @returns 深度状态对象
         */
        static getState(zWrite = true, zTestCFunc = FIREFLYX.CompareFunc.LESS) {
            let stateKey = `${zTestCFunc}_${zWrite}`;
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new DepthState();
                state.states = [zTestCFunc == null ? 0 : zTestCFunc, Number(zWrite)];
            }
            return state;
        }
    }
    DepthState.cacheStateMap = {};
    FIREFLYX.DepthState = DepthState;
})(FIREFLYX || (FIREFLYX = {}));
//http://www.jiazhengblog.com/blog/2017/01/04/2989/
var FIREFLYX;
(function (FIREFLYX) {
    /** 混合因子 */
    let BlendFactor;
    (function (BlendFactor) {
        /** 值为 0 */
        BlendFactor[BlendFactor["ZERO"] = WebGL2RenderingContext.ZERO] = "ZERO";
        /** 值为 1 */
        BlendFactor[BlendFactor["ONE"] = WebGL2RenderingContext.ONE] = "ONE";
        /** 值为 当前像素RGBA */
        BlendFactor[BlendFactor["SRC_COLOR"] = WebGL2RenderingContext.SRC_COLOR] = "SRC_COLOR";
        /** 值为 (1 - 当前像素RGBA) */
        BlendFactor[BlendFactor["ONE_MINUS_SRC_COLOR"] = WebGL2RenderingContext.ONE_MINUS_SRC_COLOR] = "ONE_MINUS_SRC_COLOR";
        /** 值为 已有像素RGBA */
        BlendFactor[BlendFactor["DST_COLOR"] = WebGL2RenderingContext.DST_COLOR] = "DST_COLOR";
        /** 值为 (1 - 已有像素RGBA) */
        BlendFactor[BlendFactor["ONE_MINUS_DST_COLOR"] = WebGL2RenderingContext.ONE_MINUS_DST_COLOR] = "ONE_MINUS_DST_COLOR";
        /** 值为 当前像素Alpha */
        BlendFactor[BlendFactor["SRC_ALPHA"] = WebGL2RenderingContext.SRC_ALPHA] = "SRC_ALPHA";
        /** 值为 (1 - 当前像素Alpha) */
        BlendFactor[BlendFactor["ONE_MINUS_SRC_ALPHA"] = WebGL2RenderingContext.ONE_MINUS_SRC_ALPHA] = "ONE_MINUS_SRC_ALPHA";
        /** 值为 已有像素Alpha */
        BlendFactor[BlendFactor["DST_ALPHA"] = WebGL2RenderingContext.DST_ALPHA] = "DST_ALPHA";
        /** 值为 (1 - 已有像素Alpha) */
        BlendFactor[BlendFactor["ONE_MINUS_DST_ALPHA"] = WebGL2RenderingContext.ONE_MINUS_DST_ALPHA] = "ONE_MINUS_DST_ALPHA";
        /** 值为 min(当前像素Alpha , 1 - 已有像素Alpha) */
        BlendFactor[BlendFactor["SRC_ALPHA_SATURATE"] = WebGL2RenderingContext.SRC_ALPHA_SATURATE] = "SRC_ALPHA_SATURATE";
    })(BlendFactor = FIREFLYX.BlendFactor || (FIREFLYX.BlendFactor = {}));
    /** 混合的方程式 */
    let BlendEquation;
    (function (BlendEquation) {
        /** 相加 */
        BlendEquation[BlendEquation["FUNC_ADD"] = WebGL2RenderingContext.FUNC_ADD] = "FUNC_ADD";
        /** 当前像素(src) 减去 已有像素(dest) */
        BlendEquation[BlendEquation["FUNC_SUBTRACT"] = WebGL2RenderingContext.FUNC_SUBTRACT] = "FUNC_SUBTRACT";
        /** 已有像素(dest) 减去 当前像素(src) */
        BlendEquation[BlendEquation["FUNC_REVERSE_SUBTRACT"] = WebGL2RenderingContext.FUNC_REVERSE_SUBTRACT] = "FUNC_REVERSE_SUBTRACT";
        /** 已有像素(dest) 与 当前像素(src) 中取最小值 */
        BlendEquation[BlendEquation["FUNC_MIN"] = WebGL2RenderingContext.MIN] = "FUNC_MIN";
        /** 已有像素(dest) 与 当前像素(src) 中取最大值 */
        BlendEquation[BlendEquation["FUNC_MAX"] = WebGL2RenderingContext.MAX] = "FUNC_MAX";
    })(BlendEquation = FIREFLYX.BlendEquation || (FIREFLYX.BlendEquation = {}));
    class BlendState {
        // eslint-disable-next-line no-useless-constructor
        constructor() {
            //
        }
        /** 当前像素因子 */
        get src() { return this.states[0]; }
        /** 已有像素因子 */
        get dst() { return this.states[1]; }
        /** 像素混合的方程式 */
        get equation() { return this.states[2]; }
        /** 当前像素Alpha因子 (值为null 无效)*/
        get srcAlpha() { return this.states[3] == 0 ? null : this.states[3]; }
        /** 已有像素Alpha因子 (值为null 无效)*/
        get dstAlpha() { return this.states[4] == 0 ? null : this.states[4]; }
        /** 像素Alpha混合的方程式 (值为null 无效)*/
        get equationAlpha() { return this.states[5] == 0 ? null : this.states[5]; }
        /**
         * 获取像素混合状态
         * @param src 当前像素因子
         * @param dst 已有像素因子
         * @param equation 像素混合的方程式
         * @param srcAlpha 当前像素Alpha因子
         * @param dstAlpha 已有像素Alpha因子
         * @param equationAlpha 像素Alpha混合的方程式
         * @returns 混合状态
         */
        static getState(src = BlendFactor.SRC_ALPHA, dst = BlendFactor.ONE_MINUS_SRC_ALPHA, equation = BlendEquation.FUNC_ADD, srcAlpha = null, dstAlpha = null, equationAlpha = BlendEquation.FUNC_ADD) {
            let stateKey = `${src}_${dst}_${equation}`;
            let alphaMode = srcAlpha != null && dstAlpha != null;
            if (alphaMode) {
                stateKey += `_${srcAlpha}_${dstAlpha}_${equationAlpha}`;
            }
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new BlendState();
                if (!alphaMode) {
                    state.states = [src, dst, equation, 0, 0, 0];
                }
                else {
                    state.states = [src, dst, equation, Number(srcAlpha), Number(dstAlpha), equationAlpha];
                }
            }
            return state;
        }
    }
    BlendState.cacheStateMap = {};
    FIREFLYX.BlendState = BlendState;
})(FIREFLYX || (FIREFLYX = {}));
//https://docs.unity.cn/cn/current/ScriptReference/Rendering.StencilState.html
var FIREFLYX;
(function (FIREFLYX) {
    /** Stencil操作选项 */
    let StencilOp;
    (function (StencilOp) {
        /** 保持当前值不变 */
        StencilOp[StencilOp["KEEP"] = WebGL2RenderingContext.KEEP] = "KEEP";
        /** 设置为 0 */
        StencilOp[StencilOp["ZERO"] = WebGL2RenderingContext.ZERO] = "ZERO";
        /** 替换为ref的值 */
        StencilOp[StencilOp["REPLACE"] = WebGL2RenderingContext.REPLACE] = "REPLACE";
        /**当前值 + ref的值*/
        StencilOp[StencilOp["INCR"] = WebGL2RenderingContext.INCR] = "INCR";
        /**当前值 + ref的值,如果到达最大值则回到0 */
        StencilOp[StencilOp["INCR_WRAP"] = WebGL2RenderingContext.INCR_WRAP] = "INCR_WRAP";
        /**当前值 - ref的值*/
        StencilOp[StencilOp["DECR"] = WebGL2RenderingContext.DECR] = "DECR";
        /**当前值 - ref的值,如果到达0则回到最大值*/
        StencilOp[StencilOp["DECR_WRAP"] = WebGL2RenderingContext.DECR_WRAP] = "DECR_WRAP";
        /** 进行按位取反操作 */
        StencilOp[StencilOp["INVERT"] = WebGL2RenderingContext.INVERT] = "INVERT";
    })(StencilOp = FIREFLYX.StencilOp || (FIREFLYX.StencilOp = {}));
    /** 模板状态 */
    class StencilState {
        // eslint-disable-next-line no-useless-constructor
        constructor() {
            //
        }
        /** 比较函数 ( 正面 )*/
        get funcFront() { return this.states[0]; }
        /** 模板测试的参考值 ( 正面 )*/
        get refFront() { return this.states[1]; }
        /** 测试时和参考值进行与运算 ( 正面 )*/
        get valueMaskFront() { return this.states[2]; }
        /** 模板测试失败时 ( 正面 )*/
        get failOpFront() { return this.states[3]; }
        /** 模板测试通过但深度测试失败时 ( 正面 )*/
        get zFailOpFront() { return this.states[4]; }
        /** 模板测试和深度测试都通过时 ( 正面 )*/
        get passOpFront() { return this.states[5]; }
        /** 在写入模板缓存时和参考值进行与运算 ( 正面 )*/
        get writeMaskFront() { return this.states[6]; }
        /** 比较函数 ( 背面 )*/
        get funcBack() { return this.states[7]; }
        /** 模板测试的参考值 ( 背面 )*/
        get refBack() { return this.states[8]; }
        /** 测试时和参考值进行与运算 ( 背面 )*/
        get valueMaskBack() { return this.states[9]; }
        /** 模板测试失败时 ( 背面 )*/
        get failOpBack() { return this.states[10]; }
        /** 模板测试通过但深度测试失败时 ( 背面 )*/
        get zFailOpBack() { return this.states[11]; }
        /** 模板测试和深度测试都通过时 ( 背面 )*/
        get passOpBack() { return this.states[12]; }
        /** 在写入模板缓存时和参考值进行与运算 ( 背面 )*/
        get writeMaskBack() { return this.states[13]; }
        /**
         * 获取模板状态
         * @param func 比较函数
         * @param ref 模板测试的参考值
         * @param valueMask 在测试时和参考值进行与运算
         * @param failOp 指定模板测试失败时
         * @param zFailOp 指定当模板测试通过但深度测试失败时
         * @param passOp 指定当模板测试和深度测试都通过时
         * @param writeMask 在写入模板缓存时和参考值进行与运算
         * @returns 模板状态对象
         */
        static getState(func = FIREFLYX.CompareFunc.ALWAYS, ref = 0, valueMask = 0xff, failOp = StencilOp.KEEP, zFailOp = StencilOp.KEEP, passOp = StencilOp.KEEP, writeMask = 0xff) {
            if (ref < 0)
                ref = 0;
            ref = Math.floor(ref);
            let stateKey = `${func}_${ref}_${valueMask}_${failOp}_${zFailOp}_${passOp}_${writeMask}`;
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new StencilState();
                state.states = [
                    //front face
                    func, ref, valueMask, failOp, zFailOp, passOp, writeMask,
                    //back face
                    func, ref, valueMask, failOp, zFailOp, passOp, writeMask
                ];
            }
            return state;
        }
        /**
         * 获取模板状态 逐面设置
         * @param func 正面 比较函数
         * @param ref 正面 模板测试的参考值
         * @param valueMask 正面 在测试时和参考值进行与运算
         * @param failOp 正面 指定模板测试失败时
         * @param zFailOp 正面 指定当模板测试通过但深度测试失败时
         * @param passOp 正面 指定当模板测试和深度测试都通过时
         * @param writeMask 正面 在写入模板缓存时和参考值进行与运算
         * @param backFunc 反面 比较函数
         * @param backRef 反面 模板测试的参考值
         * @param backValueMask 反面 在测试时和参考值进行与运算
         * @param backFailOp 反面 指定模板测试失败时
         * @param backZFailOp 反面 指定当模板测试通过但深度测试失败时
         * @param backPassOp 反面 指定当模板测试和深度测试都通过时
         * @param backWriteMask 反面 在写入模板缓存时和参考值进行与运算
         * @returns 模板状态对象
         */
        static getStateFace(func = FIREFLYX.CompareFunc.ALWAYS, ref = 0, valueMask = 0xff, failOp = StencilOp.KEEP, zFailOp = StencilOp.KEEP, passOp = StencilOp.KEEP, writeMask = 0xff, backFunc = FIREFLYX.CompareFunc.ALWAYS, backRef = 0, backValueMask = 0xff, backFailOp = StencilOp.KEEP, backZFailOp = StencilOp.KEEP, backPassOp = StencilOp.KEEP, backWriteMask = 0xff) {
            //
            if (ref < 0)
                ref = 0;
            if (backRef < 0)
                backRef = 0;
            ref = Math.floor(ref);
            backRef = Math.floor(backRef);
            // eslint-disable-next-line max-len
            let stateKey = `${func}_${ref}_${valueMask}_${failOp}_${zFailOp}_${passOp}_${writeMask}_${backFunc}_${backRef}_${backValueMask}_${backFailOp}_${backZFailOp}_${backPassOp}_${backWriteMask}`;
            let state = this.cacheStateMap[stateKey];
            if (!state) {
                this.cacheStateMap[stateKey] = state = new StencilState();
                state.states = [
                    //front face
                    func, ref, valueMask, failOp, zFailOp, passOp, writeMask,
                    //back face
                    backFunc, backRef, backValueMask, backFailOp, backZFailOp, backPassOp, backWriteMask
                ];
            }
            return state;
        }
    }
    StencilState.cacheStateMap = {};
    FIREFLYX.StencilState = StencilState;
})(FIREFLYX || (FIREFLYX = {}));
/* eslint-disable complexity */
/// <reference path="../Rendering/rasterState.ts" />
/// <reference path="../Rendering/depthState.ts" />
/// <reference path="../Rendering/blendState.ts" />
/// <reference path="../Rendering/stencilState.ts" />
/// <reference path="./asset.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** 比较函数 */
    let CompareFunc;
    (function (CompareFunc) {
        /** 从不通过. */
        CompareFunc[CompareFunc["NEVER"] = WebGL2RenderingContext.NEVER] = "NEVER";
        /** 当新值小于旧值时通过. */
        CompareFunc[CompareFunc["LESS"] = WebGL2RenderingContext.LESS] = "LESS";
        /** 当值相等时通过. */
        CompareFunc[CompareFunc["EQUAL"] = WebGL2RenderingContext.EQUAL] = "EQUAL";
        /** 当新值小于或等于旧值时通过. */
        CompareFunc[CompareFunc["LESS_EQUAL"] = WebGL2RenderingContext.LEQUAL] = "LESS_EQUAL";
        /** 当新值大于旧值时通过. */
        CompareFunc[CompareFunc["GREATER"] = WebGL2RenderingContext.GREATER] = "GREATER";
        /** 当值不同时通过. */
        CompareFunc[CompareFunc["NOT_EQUAL"] = WebGL2RenderingContext.NOTEQUAL] = "NOT_EQUAL";
        /** 当新值大于或等于旧值时通过. */
        CompareFunc[CompareFunc["GREATER_EQUAL"] = WebGL2RenderingContext.GEQUAL] = "GREATER_EQUAL";
        /** 始终通过. */
        CompareFunc[CompareFunc["ALWAYS"] = WebGL2RenderingContext.ALWAYS] = "ALWAYS";
    })(CompareFunc = FIREFLYX.CompareFunc || (FIREFLYX.CompareFunc = {}));
    /** 渲染材质资源 */
    class Material extends FIREFLYX.Asset {
        constructor() {
            super(...arguments);
            /** 光栅状态 */
            this.rasterState = FIREFLYX.RasterState.getState();
            /** 深度状态 */
            this.depthState = FIREFLYX.DepthState.getState();
            /** 混合状态 (未开启为null) */
            this.blendState = null;
            /** 模板状态 (未开启为null) */
            this.stencilState = null;
            /** 渲染队列值 */
            this.renderQueue = 1000;
            //属性值容器
            this._propertyMap = {};
            //材质 UBO 字典
            this._uboMap = {};
            //空map
            this._emptyMap = {};
            //
            this._lightMode = "";
            //能修改
            this._lockModify = false;
            //材质自己的 关键字
            this._keyword = FIREFLYX.ShaderKeyword.Get();
        }
        /**
         * 获取 材质 KeywordID
         * @returns 材质 KeywordID
         */
        getKeywordID() {
            return this._keyword.chainID;
        }
        /** 着色器资源对象 */
        get shader() { return this._shader; }
        set shader(val) {
            if (this._lockModify)
                return;
            if (this._shader == val)
                return;
            this._shader = val;
            //reset
            const ubokeys = Object.keys(this._uboMap);
            if (ubokeys.length > 0) {
                //清理 ubo
                const gl = FIREFLYX.App.webgl2;
                for (let i = 0, len = ubokeys.length; i < len; i++) {
                    const key = ubokeys[i];
                    const ubo = this._uboMap[key];
                    if (!ubo)
                        continue;
                    gl.deleteBuffer(ubo);
                }
                this._uboMap = {};
            }
            this._propertyDirtyMap = null;
            this._chunkDiryMap = null;
        }
        /** 属性字典 */
        get propertyMap() { return this._propertyMap; }
        /** 默认属性字典 ，来自于shader*/
        get defaultPropertyMap() {
            if (!this._shader)
                return null;
            return this._shader.propertyMap;
        }
        /** 材质 lightMode */
        get lightMode() {
            if (this._lightMode)
                return this._lightMode;
            return this._shader ? this._shader.lightMode : "";
        }
        set lightMode(val) {
            if (this._lockModify)
                return;
            if (val == null)
                val = "";
            this._lightMode = val;
        }
        /** 锁定修改(材质不能被修改) */
        get lockModify() { return this._lockModify; }
        set lockModify(val) { this._lockModify = val; }
        /**
         * 设置 材质的属性值, 用于对应shader unifrom 全局字段
         * @param name 属性名字
         * @param value 值
         */
        setProperty(name, value) {
            if (this._lockModify)
                return;
            if (value == this._propertyMap[name]) {
                if (!this._propertyDirtyMap)
                    this._propertyDirtyMap = {};
                this._propertyDirtyMap[name] = true;
                return;
            }
            const isSuccess = FIREFLYX.ShaderUtil.SetProperty(this._propertyMap, name, value);
            if (isSuccess) {
                const pointIdx = name.indexOf(".");
                if (pointIdx != -1) {
                    const chunkN = name.substring(0, pointIdx);
                    if (!this._chunkDiryMap)
                        this._chunkDiryMap = {};
                    this._chunkDiryMap[chunkN] = true;
                }
            }
            // return;
            // if (!name) { console.log(`setProperty invalid , property name is null.`); }
            // if (value == this._propertyMap[name]) {
            //     if (!this._propertyDirtyMap) this._propertyDirtyMap = {};
            //     this._propertyDirtyMap[name] = true;
            //     return;
            // }
            // if (value == null) {
            //     delete this._propertyMap[name];
            //     return;
            // }
            // const pointIdx = name.indexOf(".");
            // if (pointIdx != -1) {
            //     const chunkN = name.substring(0, pointIdx);
            //     if (!this._chunkDiryMap) this._chunkDiryMap = {};
            //     this._chunkDiryMap[chunkN] = true;
            // }
            // //是否是 materialValue[]?
            // if (!Array.isArray(value)) {
            //     this._propertyMap[name] = this.getUseValue(value);
            // } else {
            //     let len = value.length;
            //     let arr = this._propertyMap[name] as (BufferSource | TextureBase)[];
            //     if (arr == null) {
            //         arr = this._propertyMap[name] = [];
            //     }
            //     arr.length = len;
            //     for (let i = 0; i < len; i++) {
            //         const subVal = value[i];
            //         arr[i] = this.getUseValue(subVal);
            //     }
            // }
        }
        /**
         * 获取 材质的属性值
         * @param name 属性名字
         * @returns 值
         */
        getProperty(name) {
            return this._propertyMap[name];
        }
        // tslint:disable-next-line: cyclomatic-complexity
        uploadUniform(renderCtx) {
            if (!this._shader) {
                console.log(`on Material.uploadUniform() , shader is null. `);
                return;
            }
            const sc = this.shader.getCompiled(this.getKeywordID());
            if (!sc) {
                console.log(`on Material.uploadUniform() , ShaderCompiled is null. `);
                return;
            }
            const sp = sc.program;
            if (!sp) {
                console.log(`on Material.uploadUniform() , program is null.`);
                return;
            }
            //
            const gl = renderCtx.webgl2;
            const texAct = renderCtx.textrueActivator;
            const ignMtrMap = renderCtx.ignoreInMaterialMap;
            const ignMtrBMap = renderCtx.ignoreInMaterialBlockMap;
            const unifUseInfoMap = renderCtx.uniformUseInfoMap;
            //设置
            //遍历所有 正在启用的 unifrom 属性
            const pMap = this.propertyMap;
            const dpMap = this.defaultPropertyMap;
            const globalDpMap = FIREFLYX.Shader.globalPropertyMap;
            const bpFunMap = renderCtx.builtinUniformMap; //内建变量map
            //处理 unifrom 的数据
            for (let i = 0, len = sc.unifInfoList.length; i < len; i++) {
                const uInfo = sc.unifInfoList[i];
                const uLoca = sc.unifLocationList[i];
                const isBlcok = sc.unifIndexIsBlockMap[i] == true;
                const uName = uInfo.name;
                //过滤 非材质处理 uniform
                if (isBlcok || ignMtrMap[uName]) {
                    continue;
                }
                // const isArray = uInfo.size > 1;
                const unifT = unifUseInfoMap[uInfo.type];
                const isTexture = unifT == null;
                let pVal = pMap[uName]; //材质变量
                if (pVal == null && bpFunMap) {
                    const bFun = bpFunMap.get(uName);
                    if (bFun)
                        pVal = bFun(); //内建变量
                }
                if (pVal == null && dpMap)
                    pVal = dpMap[uName]; //默认变量
                if (pVal == null && globalDpMap)
                    pVal = globalDpMap[uName]; //全局默认变量
                if (pVal == null) {
                    //console.warn(`material "${this.name}" of uniform "${uName}" Get parameter is null.`);
                    //continue;
                    //空填默认值
                    if (isTexture) {
                        pVal = FIREFLYX.DefaultAsset.GetTexture(FIREFLYX.DefTexture.WHITE);
                    }
                    else {
                        pVal = unifT.defVal;
                    }
                }
                if (!isTexture) {
                    const isMatrix = unifT.lenght > 4;
                    if (!isMatrix) {
                        unifT.uploadFun(uLoca, pVal);
                    }
                    else {
                        const transpose = false;
                        unifT.uploadFun(uLoca, transpose, pVal);
                    }
                }
                else {
                    let tex = pVal;
                    switch (uInfo.type) {
                        case gl.SAMPLER_2D:
                        case gl.SAMPLER_CUBE:
                            const succ = texAct.activeTexOnce();
                            const texTarget = uInfo.type == gl.SAMPLER_CUBE ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
                            if (succ) {
                                gl.bindTexture(texTarget, tex.glTextrue);
                                gl.uniform1i(uLoca, texAct.currActiveTexID);
                            }
                            else {
                                console.warn(`shader sampler Quantity exceeds limit , The maximum supported number of this device is ${texAct.maxCount}, unifrom name : ${uName}`);
                            }
                            break;
                        case gl.SAMPLER_3D: break;
                        case gl.SAMPLER_2D_SHADOW: break;
                        case gl.SAMPLER_2D_ARRAY: break;
                        case gl.SAMPLER_2D_ARRAY_SHADOW: break;
                        default:
                    }
                }
            }
            let uboBindingIndex = FIREFLYX.UBOBindingEnum.MATERIAL_START;
            const emptyMap = this._emptyMap;
            const cDrityMap = this._chunkDiryMap;
            //处理 unifrom block 的数据
            for (let i = 0, len = sc.unifBlockInfoList.length; i < len; i++) {
                const blockInfo = sc.unifBlockInfoList[i];
                const bName = blockInfo.name;
                //过滤 非材质处理 uniform
                if (ignMtrBMap[bName]) {
                    continue;
                }
                let isblockDirty = cDrityMap && cDrityMap[bName];
                //block 索引于 binding index 关联
                gl.uniformBlockBinding(sp, i, uboBindingIndex);
                //获取 ubo
                let ubo = this._uboMap[bName];
                if (!ubo) {
                    //初始化 ubo
                    isblockDirty = true;
                    ubo = this._uboMap[bName] = gl.createBuffer();
                    gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
                    gl.bufferData(gl.UNIFORM_BUFFER, blockInfo.dSize, gl.DYNAMIC_DRAW); //初始化 buffer 设置大小, 通过API 获取 大小
                }
                else {
                    gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
                }
                if (isblockDirty) {
                    //设置数据到ubo
                    renderCtx.uboDataSet(sp, i, pMap, dpMap || globalDpMap || emptyMap);
                }
                //ubo 和 binding index 关联,确定定最终block使用的数据
                gl.bindBufferRange(gl.UNIFORM_BUFFER, uboBindingIndex, ubo, 0, blockInfo.dSize);
                //索引增加 为下一个block使用
                uboBindingIndex++;
            }
            //ubo 数据更新完成,ubo bind 设为空
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
            //清理 dirty Map
            this._chunkDiryMap = null;
            this._propertyDirtyMap = null;
        }
        /**
         * 启用 关键字
         * @param keyword 关键字
         */
        enableKeyword(keyword) {
            if (!keyword)
                return;
            this._keyword = this._keyword.enable(keyword);
        }
        /**
         * 禁用 关键字
         * @param keyword 关键字
         */
        disableKeyword(keyword) {
            if (!keyword)
                return;
            this._keyword = this._keyword.disable(keyword);
        }
        /**
         * 关键字开关
         * @param keyword 关键字
         * @param enable 是否开启
         */
        switchKeyword(keyword, enable) {
            if (enable) {
                this.enableKeyword(keyword);
            }
            else {
                this.disableKeyword(keyword);
            }
        }
        getUseValue(val) {
            if (typeof (val) == `object`) {
                if (val.byteLength != null) {
                    return val;
                }
                else if (val.asArray != null) {
                    const arr = val.asArray();
                    return Array.isArray(arr) ? new Float32Array(arr) : arr;
                }
                //纹理
                return val;
            }
            //is number
            return new Float32Array([val]);
        }
    }
    FIREFLYX.Material = Material;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 顶点着色器 Attributes 布局位置值 */
    let AttributeLayout;
    (function (AttributeLayout) {
        /** 顶点坐标 */
        AttributeLayout[AttributeLayout["POSITION"] = 0] = "POSITION";
        /** 法线坐标 */
        AttributeLayout[AttributeLayout["NORMAL"] = 1] = "NORMAL";
        /** 切线坐标 */
        AttributeLayout[AttributeLayout["TANGENT"] = 2] = "TANGENT";
        /** 纹理坐标0 */
        AttributeLayout[AttributeLayout["TEXCOORD_0"] = 3] = "TEXCOORD_0";
        /** 颜色坐标0 */
        AttributeLayout[AttributeLayout["COLOR_0"] = 4] = "COLOR_0";
        /** 骨骼关节坐标0 */
        AttributeLayout[AttributeLayout["JOINTS_0"] = 5] = "JOINTS_0";
        /** 骨骼关节权重坐标0 */
        AttributeLayout[AttributeLayout["WEIGHTS_0"] = 6] = "WEIGHTS_0";
        /** 纹理坐标1 */
        AttributeLayout[AttributeLayout["TEXCOORD_1"] = 7] = "TEXCOORD_1";
        /** 颜色坐标1 */
        AttributeLayout[AttributeLayout["COLOR_1"] = 8] = "COLOR_1";
        /** 纹理坐标2 */
        AttributeLayout[AttributeLayout["TEXCOORD_2"] = 9] = "TEXCOORD_2";
    })(AttributeLayout = FIREFLYX.AttributeLayout || (FIREFLYX.AttributeLayout = {}));
    /** 网格数据 */
    class MeshData {
        constructor() {
            /** 位置坐标的最小值 */
            this.posMin = new FIREFLYX.Vector3();
            /** 位置坐标的最大值 */
            this.posMax = new FIREFLYX.Vector3();
        }
        /** 计算位置坐标的 最大与最小值 */
        calcPosBoundary() {
            if (!this.posArr)
                return;
            let minX = Number.POSITIVE_INFINITY;
            let minY = Number.POSITIVE_INFINITY;
            let minZ = Number.POSITIVE_INFINITY;
            let maxX = Number.NEGATIVE_INFINITY;
            let maxY = Number.NEGATIVE_INFINITY;
            let maxZ = Number.NEGATIVE_INFINITY;
            for (let i = 0, len = this.posArr.length; i < len; i += 3) {
                let x = this.posArr[i];
                let y = this.posArr[i + 1];
                let z = this.posArr[i + 2];
                minX = Math.min(minX, x);
                minY = Math.min(minX, y);
                minZ = Math.min(minX, z);
                maxX = Math.max(maxX, x);
                maxY = Math.max(maxY, x);
                maxZ = Math.max(maxZ, x);
            }
            this.posMin.set(minX, minY, minZ);
            this.posMax.set(maxX, maxY, maxZ);
        }
        /**
         * 获取 制定三角形顶点索引数组的 类型
         * @param array 三角形顶点索引数组
         * @returns [Uint32Array | Uint16Array | Uint8Array]
         */
        static GetTriIndexBufferTypeByPosLen(positionCount) {
            if (positionCount == null)
                return Uint32Array;
            const u16Max = 65535;
            const u8Max = 255;
            return positionCount > u16Max ? Uint32Array : positionCount > u8Max ? Uint16Array : Uint8Array;
        }
    }
    FIREFLYX.MeshData = MeshData;
    /** 网格的部分基本单元 */
    class MeshPrimitive {
    }
    FIREFLYX.MeshPrimitive = MeshPrimitive;
    /** 模型网格资源 */
    class Mesh extends FIREFLYX.Asset {
        constructor() {
            super(...arguments);
            this._elementLength = 0;
            this._elementType = -1;
            /** 网格部分基本单元列表 */
            this.primitives = [];
        }
        /** webgl 的顶点缓冲区对象 */
        get glPosBuffer() { return this._glPosBuffer; }
        /** webgl 的UV缓冲区对象 */
        get glUVBuffer() { return this._glUVBuffer; }
        /** webgl 的法线缓冲区对象 */
        get glNormalBuffer() { return this._glNormalBuffer; }
        /** webgl 的切线缓冲区对象 */
        get glTangentBuffer() { return this._glTangentBuffer; }
        /** webgl 的顶点索引缓冲区对象 */
        get glIndexBuffer() { return this._glIndexBuffer; }
        /** webgl 的顶点索引数量 */
        get elementLenght() { return this._elementLength; }
        /** webgl 的顶点索引元素类型常量 , -1 表示不使用 顶点索引*/
        get elementType() { return this._elementType; }
        /** webgl VAO 对象 */
        get VAO() { return this._VAO; }
        /** 提交使用当前网格数据 */
        apply() {
            let mData = this.data;
            if (!mData) {
                console.warn(`apply fial , data is null.`);
                return;
            }
            let gl = FIREFLYX.App.webgl2;
            if (!gl) {
                console.warn(`apply fial , gl is null.`);
                return;
            }
            if (!mData.posArr || mData.posArr.length < 1) {
                console.warn(`apply fial , vertex buffer is empty.`);
                return;
            }
            //设置顶点数据
            let setGLData = (glBuffer, data, target = gl.ARRAY_BUFFER, usage = gl.STATIC_DRAW) => {
                if (data.byteLength <= 0) {
                    //data invalid
                    if (glBuffer != null)
                        gl.deleteBuffer(glBuffer); //clear buffer
                    return null;
                }
                if (glBuffer == null)
                    glBuffer = gl.createBuffer();
                gl.bindBuffer(target, glBuffer);
                gl.bufferData(target, data, usage); //data set to webglBuffer
                return glBuffer;
            };
            //绑定激活Attrib
            let setPointer = (aLayout, size, type = gl.FLOAT) => {
                gl.vertexAttribPointer(aLayout, size, type, false, 0, 0); //数据不够时会填充默认值
                //连接启用  分配个 变量的 缓冲区对象
                gl.enableVertexAttribArray(aLayout);
            };
            //VAO 模式
            //create vao
            this._VAO = gl.createVertexArray();
            //bind VAO , 记录VBO 的操作
            gl.bindVertexArray(this._VAO);
            //pos
            this._glPosBuffer = setGLData(this._glPosBuffer, mData.posArr);
            setPointer(AttributeLayout.POSITION, 3);
            //uv
            if (mData.uvArr) {
                this._glUVBuffer = setGLData(this._glUVBuffer, mData.uvArr);
                setPointer(AttributeLayout.TEXCOORD_0, 2);
            }
            //uv1
            if (mData.uvArr1) {
                this._glUV1Buffer = setGLData(this._glUV1Buffer, mData.uvArr1);
                setPointer(AttributeLayout.TEXCOORD_1, 2);
            }
            //uv2
            if (mData.uvArr2) {
                this._glUV2Buffer = setGLData(this._glUV2Buffer, mData.uvArr2);
                setPointer(AttributeLayout.TEXCOORD_2, 2);
            }
            //normal
            if (mData.normalArr) {
                this._glNormalBuffer = setGLData(this._glNormalBuffer, mData.normalArr);
                setPointer(AttributeLayout.NORMAL, 3);
            }
            //tangent
            if (mData.tangentArr) {
                this._glTangentBuffer = setGLData(this._glTangentBuffer, mData.tangentArr);
                setPointer(AttributeLayout.TANGENT, 4);
            }
            //index
            if (mData.triIndexArr) {
                let idxD = mData.triIndexArr;
                this._glIndexBuffer = setGLData(this._glIndexBuffer, idxD, gl.ELEMENT_ARRAY_BUFFER);
                //elem length
                this._elementLength = idxD.length;
            }
            else {
                this._elementLength = 0;
            }
            //index elemnet type
            this.setTriIndexType();
            //检查添加默认Primitive
            if (this.primitives.length <= 0) {
                this.setDefPrimitive();
            }
            else {
                //更新meshPrimitive length
                const mp = this.primitives[this.primitives.length - 1];
                this.adjustPrimitiveLen(mp);
            }
            //clear
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
            // gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
            gl.bindVertexArray(null);
        }
        /**
         * 设置调整 MeshPrimitive 的length
         * @param mp MeshPrimitive对象
         */
        adjustPrimitiveLen(mp) {
            var _a, _b;
            let allLen = 0;
            if (this._elementLength > 0) {
                allLen = this._elementLength;
            }
            else {
                if ((_a = this.data) === null || _a === void 0 ? void 0 : _a.posArr) {
                    allLen = (((_b = this.data) === null || _b === void 0 ? void 0 : _b.posArr.length) / 3) || 0;
                }
            }
            mp.length = allLen - mp.start;
            if (mp.length < 0)
                mp.length = 0;
        }
        //补全默认Primitive设置
        setDefPrimitive() {
            let p = new MeshPrimitive();
            p.material = 0;
            p.start = 0;
            p.mode = FIREFLYX.App.webgl2.TRIANGLES;
            p.frontFace = FIREFLYX.App.webgl2.CCW;
            this.adjustPrimitiveLen(p);
            this.primitives.push(p);
        }
        /** 设置 顶点索引元素类型常量*/
        setTriIndexType() {
            let d = this.data;
            this._elementType = -1;
            if (!d || !d.triIndexArr)
                return;
            let byteSize = d.triIndexArr.byteLength / d.triIndexArr.length;
            this._elementType = byteSize == 4 ? Mesh.UNSIGNED_INT : byteSize == 2 ? Mesh.UNSIGNED_SHORT : Mesh.UNSIGNED_BYTE;
        }
    }
    /** element 使用 webgl UNSIGNED_INT 类型标识*/
    Mesh.UNSIGNED_INT = 5125;
    /** element 使用 webgl UNSIGNED_SHORT 类型标识*/
    Mesh.UNSIGNED_SHORT = 5123;
    /** element 使用 webgl UNSIGNED_BYTE 类型标识*/
    Mesh.UNSIGNED_BYTE = 5121;
    /** element 空类型 */
    Mesh.ELEMENT_NULL_TYPE = -1;
    FIREFLYX.Mesh = Mesh;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * shader uniform 属性对象
     */
    class ShaderProperty {
    }
    /**
     * 编译后的着色器对象
     */
    class ShaderCompiled {
        /**
         * 传入着色器代码 并立即编译
         * @param vs 顶点着色器代码
         * @param fs 片元着色器代码
         * @param keywordIterator0 关键词迭代器0
         * （为了优化不用 ...keywordIterator 来实现）
         * @param keywordIterator1 关键词迭代器1
         * @param keywordIterator2 关键词迭代器2
         */
        constructor(vs, fs, keywordIterator0 = null, keywordIterator1 = null, keywordIterator2 = null) {
            this._unifInfoList = [];
            this._unifLocationList = [];
            this._unifBlockInfoList = [];
            this._unifIndexIsBlockMap = {};
            this._unifBlockNameIndexMap = {};
            const gl = FIREFLYX.App.webgl2;
            //keywords 设置 define
            let defKWStr = ``;
            //打上变体 宏定义
            defKWStr += ShaderCompiled.Getkeyword2Str(keywordIterator0);
            defKWStr += ShaderCompiled.Getkeyword2Str(keywordIterator1);
            defKWStr += ShaderCompiled.Getkeyword2Str(keywordIterator2);
            //es 300 标记检测 , define 插入
            let idx = ShaderCompiled.GetEs300Index(vs);
            vs = `${vs.substring(0, idx)}\n${defKWStr}${vs.substring(idx)}`;
            idx = ShaderCompiled.GetEs300Index(fs);
            fs = `${fs.substring(0, idx)}\n${defKWStr}${fs.substring(idx)}`;
            //编译
            this._program = ShaderCompiled.InitProgram(gl, vs, fs);
            //初始化 着色器属性信息
            this.initPropertyInfos();
        }
        /** 着色器程序 编译后的 WebGLProgram 对象   */
        get program() {
            if (!this._program)
                return ShaderCompiled.errorFallBack._program;
            return this._program;
        }
        /** 着色器 unifrom 信息列表 */
        get unifInfoList() { return this._unifInfoList; }
        /** 着色器 unifrom 地址对象列表 */
        get unifLocationList() { return this._unifLocationList; }
        /** 着色器 unifrom block 信息列表 */
        get unifBlockInfoList() { return this._unifBlockInfoList; }
        /** 着色器 unifrom 索引 字典 {unifrom索引 : 是block}*/
        get unifIndexIsBlockMap() { return this._unifIndexIsBlockMap; }
        /** 着色器 unifrom block 名 索引 字典 {block名 : block索引} */
        get unifBlockNameIndexMap() { return this._unifBlockNameIndexMap; }
        /** 错误的FallBack ShaderCompiled实例 */
        static get errorFallBack() {
            let errFB = ShaderCompiled._errorFallBack;
            if (!errFB) {
                const vsCode = `#version 300 es
                precision mediump float;
                layout (location = 0) in vec4 a_Position;   //顶点
                uniform ubo_model_block{
                    mat4 u_mMtx;                            //模型矩阵
                } uboModel;
                uniform ubo_cam_block{
                    mat4 u_viewMtx;                         //视窗矩阵
                    mat4 u_projectMtx;                      //投影矩阵
                    vec4 u_eyePos;                          //眼睛位置
                } uboCam;
                void main() {
                    mat4 mvpMat = uboCam.u_projectMtx * uboCam.u_viewMtx * uboModel.u_mMtx;
                    gl_Position = mvpMat * a_Position;
                }
                `;
                const fsCode = `#version 300 es
                precision mediump float;
                out vec4 color;
                void main(){
                    color = vec4(1.,0.,1.,1.);              //仅输出紫色
                }
                `;
                //
                errFB = new ShaderCompiled(vsCode, fsCode);
                ShaderCompiled._errorFallBack = errFB;
            }
            return errFB;
        }
        /** 初始化 属性信息 */
        initPropertyInfos() {
            if (!this._program)
                return;
            const gl = FIREFLYX.App.webgl2;
            const p = this._program;
            //收集 着色器 属性
            //unifrom
            const unifNum = gl.getProgramParameter(p, gl.ACTIVE_UNIFORMS);
            const unifBlockNum = gl.getProgramParameter(p, gl.ACTIVE_UNIFORM_BLOCKS);
            const unifInfoList = [];
            const unifLocationList = [];
            for (let i = 0; i < unifNum; i++) {
                const unifInfo = gl.getActiveUniform(p, i);
                const unifLoc = gl.getUniformLocation(p, unifInfo.name);
                unifInfoList.push(unifInfo);
                unifLocationList.push(unifLoc);
            }
            const blockInfoList = [];
            const blockMap = {};
            const blockNameIndex = {};
            for (let i = 0; i < unifBlockNum; i++) {
                const name = gl.getActiveUniformBlockName(p, i);
                blockNameIndex[name] = i;
                const dSize = gl.getActiveUniformBlockParameter(p, i, gl.UNIFORM_BLOCK_DATA_SIZE);
                const unifIdxs = gl.getActiveUniformBlockParameter(p, i, gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES);
                unifIdxs.forEach((idx) => { blockMap[idx] = true; });
                blockInfoList.push({ name, dSize, unifIdxs });
            }
            //
            this._unifInfoList = unifInfoList;
            this._unifLocationList = unifLocationList;
            this._unifBlockInfoList = blockInfoList;
            this._unifIndexIsBlockMap = blockMap;
            this._unifBlockNameIndexMap = blockNameIndex;
        }
        /**
         * 初始化着色器
         * @param gl WebGL2RenderingContext 对象
         * @param vs 顶点着色器代码
         * @param fs 片元着色器代码
         * @returns WebGLProgram 对象
         */
        static InitProgram(gl, vs, fs) {
            let shaderCompileFailed = false;
            let shaderLinkFailed = false;
            //顶点shader
            //创建webglShader 对象
            let vsSh = gl.createShader(gl.VERTEX_SHADER);
            if (!vsSh) {
                console.error(`createShader(VERTEX_SHADER) fail.`);
                return null;
            }
            //将字符串程序代码传入
            gl.shaderSource(vsSh, vs);
            //编译字符串程序代码成二进制对象,便于webgl使用
            gl.compileShader(vsSh);
            if (!this.CkShaderCompileS(gl, vsSh, "Vertex Shader")) {
                shaderCompileFailed = true;
            }
            //片源shader
            let fsSh = gl.createShader(gl.FRAGMENT_SHADER);
            if (!fsSh) {
                console.error(`createShader(FRAGMENT_SHADER) fail.`);
                return null;
            }
            gl.shaderSource(fsSh, fs);
            gl.compileShader(fsSh);
            if (!this.CkShaderCompileS(gl, fsSh, "Fragment Shader")) {
                shaderCompileFailed = true;
            }
            //创建WebGLProgram
            let pVs = gl.createProgram();
            if (!pVs) {
                console.error(`createProgram() fail.`);
                return null;
            }
            //附加 shader程序
            gl.attachShader(pVs, vsSh);
            gl.attachShader(pVs, fsSh);
            //如果 vsSh ， fsSh 不需要缓存，可 gl.deleteShader(vsSh);
            //shader err log
            let log = gl.getShaderInfoLog(vsSh);
            if (log) {
                console.error(`${vs} \n ${log}`);
            }
            log = gl.getShaderInfoLog(fsSh);
            if (log) {
                console.error(`${fs} \n ${log}`);
            }
            //链接到一个已经 attach的程序
            gl.linkProgram(pVs);
            // //添加指定的程序 到当前的渲染状态中去 (为什么 要 link 又要 use ?)
            // gl.useProgram(pVs);
            let r3 = gl.getProgramParameter(pVs, gl.LINK_STATUS);
            if (r3 == false) {
                // console.error("vs:" + nameVS + "   fs:" + nameFS + "a webgl program error:" + webgl.getProgramInfoLog(program));
                console.error("a webgl program error:" + gl.getProgramInfoLog(pVs));
                gl.deleteProgram(pVs);
                shaderLinkFailed = true;
                // return null;
            }
            const hasErrStr = this.CatchErrorStr(gl);
            if (hasErrStr || shaderLinkFailed || shaderCompileFailed) {
                return null;
            }
            return pVs;
        }
        /**
         * 获取 keyword 转到 宏定义 字符串
         * @param keywordIterator keyword迭代器
         * @returns 宏定义 字符串
         */
        static Getkeyword2Str(keywordIterator) {
            let result = ``;
            if (!keywordIterator)
                return result;
            let next;
            while (!(next = keywordIterator.next()).done) {
                const keywordStr = next.value;
                if (!keywordStr)
                    continue;
                result += `#define ${keywordStr}\n`;
            }
            return result;
        }
        /**
         * 获取ES 300 标记索引
         * @param shaderCode
         * @returns
         */
        static GetEs300Index(shaderCode) {
            const regEs300 = /#version *300 *es *[\n|\r]/;
            let idx = 0;
            let ex = regEs300.exec(shaderCode);
            if (ex && ex.length > 0) {
                idx = ex[0].length;
            }
            return idx;
        }
        /** 检查shader编译 */
        static CkShaderCompileS(gl, sh, info = "") {
            let s = gl.getShaderParameter(sh, gl.COMPILE_STATUS);
            if (s == false) {
                console.error(`compiles shader error : ${info}`);
                return false;
            }
            return true;
        }
        /** 获得shader编译错误信息 并打印 */
        static CatchErrorStr(gl) {
            let str = ``;
            let val = gl.getError();
            switch (val) {
                case gl.INVALID_ENUM:
                    str = `An unacceptable value has been specified for an enumerated argument. `;
                    break;
                case gl.INVALID_VALUE:
                    str = `A numeric argument is out of range. `;
                    break;
                case gl.INVALID_OPERATION:
                    str = `The specified command is not allowed for the current state. `;
                    break;
                case gl.INVALID_FRAMEBUFFER_OPERATION:
                    str = `The currently bound framebuffer is not framebuffer complete when trying to render to or to read from it.`;
                    break;
                case gl.OUT_OF_MEMORY:
                    str = `Not enough memory is left to execute the command.`;
                    break;
                case gl.CONTEXT_LOST_WEBGL:
                    str = `If the WebGL context is lost, this error is returned on the first call to getError.
                                                   Afterwards and until the context has been restored, it returns gl.NO_ERROR.`;
                    // tslint:disable-next-line: align
                    break;
                default:
            }
            if (str != ``)
                console.error(`webgl error : ${str}`);
            return str != ``;
        }
    }
    FIREFLYX.ShaderCompiled = ShaderCompiled;
    /** 字典树节点 */
    class KeywordTrieNode {
        constructor(key) {
            this._key = key;
        }
        static getChainID(chainKey) {
            let id = this._ChainKeyIDMap.get(chainKey);
            if (id == null) {
                id = this._chainIDCount++;
                this._ChainKeyIDMap.set(chainKey, id);
            }
            return id;
        }
        /** id */
        get chainID() { return this._chainID; }
        /** 键值 */
        get key() { return this._key; }
        /** 子节点字典容器 */
        get childMap() {
            if (this._childMap == null) {
                this._childMap = new Map();
            }
            return this._childMap;
        }
        /** 刷新ChainID */
        refreshChainID() {
            if (this.chainID == -1)
                return; //root节点跳过
            //从头顺一遍
            let node = this;
            //更新 chainKey
            // this._chainKey = keys.sort().toString();
            let chainkey = ``;
            if (node) {
                if (node.parent) {
                    chainkey = `${node.parent._chainKey}_${node.key}`;
                }
                else {
                    chainkey = `${node.key}`;
                }
            }
            //
            // let keys: string[] = [];
            // while (node && node.parent) {
            //     keys.push(node.key);
            //     node = node.parent;
            // }
            // chainkey = keys.toString();
            this._chainKey = chainkey;
            this._chainID = KeywordTrieNode.getChainID(this._chainKey);
        }
        add(node) {
            let cMap = this.childMap;
            node.parent = this;
            cMap.set(node.key, node);
        }
        remove(keyword) {
            //
        }
    }
    /** 链ID */
    KeywordTrieNode._chainIDCount = 0;
    KeywordTrieNode._ChainKeyIDMap = new Map();
    /** 字典树 */
    class KeywordTrieTree {
        constructor() {
            this._chainIDNodeMap = new Map();
            this._rootNode = new KeywordTrieNode(``);
        }
        // public getChainID(keywords: string[]): number {
        //     //
        //     return -1;
        // }
        /**
         * 获取一个合并指定关键字的chainID
         * @param srcChainID 被合并的chainID
         * @param keyword 合并的指定关键字
         * @returns 返回新的chainID
         */
        chainIDMergeKeyword(srcChainID, keyword) {
            //先找map中是否有节点
            let rootNode = this._chainIDNodeMap.get(srcChainID);
            if (!rootNode)
                rootNode = this._rootNode;
            let tarNode;
            //看一下子节点中是否有 key
            const tempNode = rootNode.childMap.get(keyword);
            if (tempNode) {
                return tempNode.chainID;
            }
            //new node
            tarNode = new KeywordTrieNode(keyword);
            rootNode.add(tarNode);
            tarNode.refreshChainID();
            const newChainID = tarNode.chainID;
            this._chainIDNodeMap.set(newChainID, tarNode);
            return newChainID;
        }
        /**
         * 获取一个拆分指定关键字的chainID
         * @param keyword 拆分的关键字
         * @param keywordMap 现有关键字容器
         * @returns 返回新的chainID
         */
        chainIDSplitKeyword(keyword, keywordMap) {
            //先找map中是否有节点
            let currNode = this._rootNode;
            let next;
            let mapKeys = keywordMap.keys();
            let childMap = currNode.childMap;
            const chainIDNodeMap = this._chainIDNodeMap;
            while (!(next = mapKeys.next()).done) {
                const k = next.value;
                //是指定keyword 跳过
                if (k == keyword || childMap.has(k))
                    continue;
                //没有的话 并建立
                let tarNode = childMap.get(k);
                tarNode = new KeywordTrieNode(k);
                currNode.add(tarNode);
                tarNode.refreshChainID();
                chainIDNodeMap.set(tarNode.chainID, tarNode);
                currNode = tarNode;
                childMap = currNode.childMap;
            }
            return currNode.chainID;
        }
    }
    /** 着色器关键字 */
    class ShaderKeyword {
        constructor(chainID) {
            // private _keywords: string[] = [];
            this._keywordMap = new Map();
            this._inited = false;
            this._chainID = chainID;
        }
        get chainID() { return this._chainID; }
        /**
         * 通过chainID 获取 ShaderKeyword对象
         * @param chainID
         * @returns ShaderKeyword对象
         */
        static Get(chainID = ShaderKeyword.DefaultChainID) {
            if (chainID == null || isNaN(chainID))
                chainID = ShaderKeyword.DefaultChainID;
            let kw = this._cacheKeywordMap.get(chainID);
            if (kw) {
                return kw;
            }
            const defKWChainID = ShaderKeyword.DefaultChainID;
            //创建一个新的keyword对象
            if (chainID != defKWChainID) {
                const newKW = new ShaderKeyword(chainID);
                this._cacheKeywordMap.set(chainID, newKW);
                return newKW;
            }
            //返回默认（-1）的keyword
            let kwDef = this._cacheKeywordMap.get(defKWChainID);
            if (!kwDef) {
                kwDef = new ShaderKeyword(defKWChainID);
                this._cacheKeywordMap.set(kwDef._chainID, kwDef);
            }
            return kwDef;
        }
        /**
         * 合并连个 ShaderKeyword 对象
         * @param a ShaderKeyword 对象 a
         * @param b ShaderKeyword 对象 b
         * @returns 合并后的 ShaderKeyword 对象
         */
        static Merge(a, b) {
            const def = ShaderKeyword.Get();
            if (a == null || b == null)
                return def;
            if ((a != null && b == null) || a == b)
                return a;
            if (b != null && a == null)
                return b;
            let result = a;
            const bKeys = b.getKeys();
            let next;
            while (!(next = bKeys.next()).done) {
                const val = next.value;
                if (!val)
                    continue;
                const aKeyWorld = val;
                result = a.enable(aKeyWorld);
            }
            return result;
        }
        initKeywordMerge(keys, addKeyword = "") {
            if (this._inited || !keys)
                return;
            let next;
            const keywordMap = this._keywordMap;
            while (!(next = keys.next()).done) {
                const val = next.value;
                if (!val)
                    continue;
                keywordMap.set(next.value, true);
            }
            //
            if (addKeyword)
                this._keywordMap.set(addKeyword, true);
            this._inited = true;
        }
        initKeywordSplit(keys, removeKeyword = "") {
            if (this._inited || !keys)
                return;
            let next;
            const keywordMap = this._keywordMap;
            while (!(next = keys.next()).done) {
                const val = next.value;
                if (val == removeKeyword)
                    continue;
                keywordMap.set(val, true);
            }
            this._inited = true;
        }
        /**
         * 启用一个关键字
         * @param keyword 关键字
         * @returns ShaderKeyword对象（可能是自己，可能是一个新的对象）
         */
        enable(keyword) {
            //当前对象已经包含 keyword 不做任何处理
            if (this._keywordMap.has(keyword))
                return this;
            const oldKW = this;
            const newChainID = ShaderKeyword._keywordTrieTree.chainIDMergeKeyword(this._chainID, keyword);
            const newKW = ShaderKeyword.Get(newChainID);
            if (!newKW._inited) {
                newKW.initKeywordMerge(oldKW.getKeys(), keyword);
            }
            return newKW;
        }
        /**
         * 取消一个关键字的启用状态
         * @param keyword 关键字
         * @returns ShaderKeyword对象（可能是自己，可能是一个新的对象）
         */
        disable(keyword) {
            //当前对象不包含 keyword 不做任何处理
            if (!this._keywordMap.has(keyword))
                return this;
            const oldKW = this;
            const newChainID = ShaderKeyword._keywordTrieTree.chainIDSplitKeyword(keyword, this._keywordMap);
            const newKW = ShaderKeyword.Get(newChainID);
            if (!newKW._inited) {
                newKW.initKeywordSplit(oldKW.getKeys(), keyword);
            }
            return newKW;
        }
        /**
         * 获取 所有keyword 的迭代器
         * @returns 所有keyword 的迭代器
         */
        getKeys() {
            return this._keywordMap.keys();
        }
    }
    ShaderKeyword.DefaultChainID = -1;
    ShaderKeyword._keywordTrieTree = new KeywordTrieTree();
    ShaderKeyword._cacheKeywordMap = new Map();
    FIREFLYX.ShaderKeyword = ShaderKeyword;
    /**
     * 着色器资源
     * shader keywords 规则参考
     * https://docs.unity3d.com/cn/current/Manual/shader-keywords.html
     */
    class Shader extends FIREFLYX.Asset {
        constructor() {
            super(...arguments);
            /** shader 识别标签 */
            this.tag = "";
            /** 光照模式标记 */
            this.lightMode = "";
            /** 文件路径 */
            this.filePath = "";
            this._isLoadAll = false;
            // private _variantMap: { [kwStr: string]: ShaderCompiled } = {};
            this._variantIDMap = {};
            // private _currKeywordStr: string = "";
            // private _currKeywords: string[] = [];
            this._propertyMap = {}; //属性值容器
        }
        /** shader的属性值字典 */
        get propertyMap() { return this._propertyMap; }
        /**  shader的全局 属性值字典  */
        static get globalPropertyMap() { return Shader._globalPropertyMap; }
        /**
         * 获取 WebGLProgram 对象
         * @param keywordStr 变体关键字
         * @returns WebGLProgram 对象
         */
        // public getProgram(keywordStr?: string) {
        //     const comp = this.getCompiled(keywordStr);
        //     if (!comp) return null;
        //     return comp.program;
        // }
        /**
         * 获取 编译了的着色器
         * @param materialKeywordID  材质的 关键字ID （不传，则忽略）
         * @param otherKeywordID  其他的 关键字ID （不传，则忽略）
         * @returns 编译了的着色器对象
         */
        getCompiled(materialKeywordID, otherKeywordID) {
            const defID = ShaderKeyword.DefaultChainID;
            const kw = Shader._keyword;
            const mKW = materialKeywordID != null && materialKeywordID != defID ? ShaderKeyword.Get(materialKeywordID) : null;
            const oKW = otherKeywordID != null && otherKeywordID != defID ? ShaderKeyword.Get(otherKeywordID) : null;
            let variantID = kw.chainID == defID ? `` : `${kw.chainID}`;
            if (mKW)
                variantID += !variantID ? `${mKW.chainID}` : `_${mKW.chainID}`;
            if (oKW)
                variantID += !variantID ? `${oKW.chainID}` : `_${oKW.chainID}`;
            let variant = this._variantIDMap[variantID];
            //JIT 模式编译shader （即时用 ，即时编译）
            if (!variant) {
                let vsCode = this.vertexGLSL.getFull();
                let fsCode = this.fragmentGLSL.getFull();
                if (vsCode && fsCode) {
                    const keys = kw.getKeys();
                    const mKeys = mKW ? mKW.getKeys() : null;
                    const oKeys = oKW ? oKW.getKeys() : null;
                    let sc = variant = new ShaderCompiled(vsCode, fsCode, keys, mKeys, oKeys);
                    this._variantIDMap[variantID] = sc;
                }
                else {
                    console.warn(`fail code is null.`);
                    //给个 紫色的错误 着色器
                    variant = ShaderCompiled.errorFallBack;
                }
            }
            return variant;
        }
        /** 是否依赖资源全部加载完毕 */
        isLoadedAll() {
            if (!this._isLoadAll && this.vertexGLSL && this.fragmentGLSL) {
                this._isLoadAll = true;
            }
            return this._isLoadAll;
        }
        /** 加载所有依赖的资源 */
        async loadAllFile() {
            if (this._isLoadAll)
                return;
            if (!this.vertexFile) {
                console.error(`can't to load ,vertexShader is null.`);
                return;
            }
            if (!this.fragmentFile) {
                console.error(`can't to load ,fragmentShader is null.`);
                return;
            }
            let _idx = this.filePath.lastIndexOf("/");
            let folderPath = `${this.filePath.substring(0, _idx)}/`;
            let allFile = [];
            //vs load
            let fUrl = `${folderPath}${this.vertexFile}`;
            if (!FIREFLYX.AssetManager.GetAsset(fUrl)) {
                allFile.push(FIREFLYX.AssetManager.LoadFile(fUrl));
            }
            //fs load
            fUrl = `${folderPath}${this.fragmentFile}`;
            if (!FIREFLYX.AssetManager.GetAsset(fUrl)) {
                allFile.push(FIREFLYX.AssetManager.LoadFile(fUrl));
            }
            let glsls = await Promise.all(allFile)
                .catch((reason) => {
                console.error(`glsl loaded fail. \n reason : ${reason}`);
            });
            //
            //load all
            if (glsls && glsls.length > 0) {
                for (let i = 0, len = glsls.length; i < len; i++) {
                    let _g = glsls[i];
                    if (_g.isLoadedAll())
                        continue;
                    await _g.loadAllFile();
                }
                //set shader
                this.vertexGLSL = glsls[0];
                this.fragmentGLSL = glsls[1];
            }
            this._isLoadAll = true;
        }
        /** 提交使用当前设置 */
        // tslint:disable-next-line: cyclomatic-complexity
        apply() {
            let vsCode = this.vertexGLSL.getFull();
            let fsCode = this.fragmentGLSL.getFull();
            // console.log(`vsCode :${vsCode}`);
            // console.log(`fsCode :${fsCode}`);
            if (!fsCode || !vsCode) {
                console.error(`apply fail code is null.`);
                return;
            }
            // //变体处理
            // const variantKWs: string[][] = [];          //变体列表（关键字列表区分） , 默认无关键字
            // let mcs = this.multiCompiles.concat();
            // let hasMC = mcs && mcs.length > 0 && mcs[0].length > 0;
            // if (hasMC) {
            //     let mc = mcs.shift();
            //     while (mc) {
            //         //扩充
            //         if (mc.length < 1) continue;
            //         if (variantKWs.length == 0) {
            //             mc.forEach((v) => variantKWs.push([v]));
            //         } else {
            //             let oldLen = variantKWs.length;
            //             variantKWs.length *= mc.length;
            //             for (let i = 0, len = variantKWs.length; i < len; i++) {
            //                 const v = variantKWs[i];
            //                 if (v) continue;
            //                 variantKWs[i] = variantKWs[i % oldLen].concat();
            //             }
            //         }
            //         //填入 新的key
            //         for (let i = 0, len = variantKWs.length; i < len; i++) {
            //             const mcVal = mc[Math.floor(i / mc.length)];
            //             let arr = variantKWs[i];
            //             if (arr.indexOf(mcVal) != -1) continue;
            //             //加入到kw组合
            //             arr.push(mcVal);
            //         }
            //         mc = mcs.shift();
            //     }
            //     variantKWs.forEach((v) => {
            //         //关键字 排序
            //         v.sort();
            //         //去掉 nokey
            //         if (v.length > 1 && v[0] == "") {
            //             v.shift();
            //         }
            //     });
            // }
            // variantKWs.unshift([""]);
            // //编译所有变体着色器
            // for (let i = 0, len = variantKWs.length; i < len; i++) {
            //     const kws = variantKWs[i];
            //     kws.sort();
            //     const kwStr = kws.toString();
            //     //init Shaders
            //     const sc = new ShaderCompiled(vsCode, fsCode, kws);
            //     this._variantMap[kwStr] = sc;
            // }
            //definePropertyMap 映射为 _propertyMap
            const defPMap = this.definePropertyMap;
            if (defPMap) {
                const pKeys = Object.keys(defPMap);
                for (let i = 0, len = pKeys.length; i < len; i++) {
                    const pKey = pKeys[i];
                    const p = defPMap[pKey];
                    if (!p || p.type == null || p.value == null)
                        continue;
                    switch (p.type) {
                        case "texture":
                            // tslint:disable-next-line: no-string-literal
                            let tEnum = FIREFLYX.DefTexture[p.value];
                            if (tEnum == null)
                                tEnum = FIREFLYX.DefTexture.WHITE;
                            this._propertyMap[pKey] = FIREFLYX.DefaultAsset.GetTexture(tEnum);
                            break;
                        default:
                            const val = p.value;
                            this._propertyMap[pKey] = Array.isArray(val) ? new Float32Array(val) : new Float32Array([val]);
                    }
                }
            }
        }
        /**
         * 启用 全局关键字
         * @param keyword 关键字
         */
        static enableKeyword(keyword) {
            if (!keyword)
                return;
            this._keyword = Shader._keyword.enable(keyword);
        }
        /**
         * 禁用 全局关键字
         * @param keyword 关键字
         */
        static disableKeyword(keyword) {
            if (!keyword)
                return;
            this._keyword = Shader._keyword.disable(keyword);
        }
        /**
         * 全局关键字开关
         * @param keyword 关键字
         * @param enable 是否开启
         */
        static switchKeyword(keyword, enable) {
            if (enable) {
                this.enableKeyword(keyword);
            }
            else {
                this.disableKeyword(keyword);
            }
        }
        /**
         * 获取 全局的Keyword ID
         * @returns shader的Keyword ID
         */
        static getKeywordID() {
            return Shader._keyword.chainID;
        }
        // /** 刷新 关键字 字符串 */
        // private reflashKeywordStr() {
        //     this._currKeywords.sort();  //为了确保出来的key是一致的
        //     this._currKeywordStr = this._currKeywords.toString();
        // }
        /**
         * 设置 全局属性值 , 用于对应shader unifrom 全局字段
         * @param name 属性名字
         * @param value 值
         */
        static setProperty(name, value) {
            FIREFLYX.ShaderUtil.SetProperty(this._globalPropertyMap, name, value);
        }
    }
    /** 全局属性 字典容器 */
    Shader._globalPropertyMap = {}; //属性值容器
    //shader 的 关键字
    Shader._keyword = ShaderKeyword.Get();
    FIREFLYX.Shader = Shader;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="../interfaces.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** 灯光类型 */
    let LightType;
    (function (LightType) {
        /** 方向光源 */
        LightType[LightType["DIRECTIONAL"] = 0] = "DIRECTIONAL";
        /** 点光源 */
        LightType[LightType["POINT"] = 1] = "POINT";
        /** 聚光灯 光源 */
        LightType[LightType["SPOT"] = 2] = "SPOT";
    })(LightType = FIREFLYX.LightType || (FIREFLYX.LightType = {}));
    /** 阴影类型 */
    let ShadowType;
    (function (ShadowType) {
        /** 没有阴影 */
        ShadowType[ShadowType["ON_SHADOW"] = 0] = "ON_SHADOW";
        /** 硬阴影 */
        ShadowType[ShadowType["HARD_SHADOW"] = 1] = "HARD_SHADOW";
        /** 软阴影阴影 */
        ShadowType[ShadowType["SOFT_SHADOW"] = 2] = "SOFT_SHADOW";
    })(ShadowType = FIREFLYX.ShadowType || (FIREFLYX.ShadowType = {}));
    /** 阴影纹理Map分辨率 */
    let ShadowMapResolution;
    (function (ShadowMapResolution) {
        /** 低 */
        ShadowMapResolution[ShadowMapResolution["LOW"] = 0] = "LOW";
        /** 中 */
        ShadowMapResolution[ShadowMapResolution["MEDIUM"] = 1] = "MEDIUM";
        /** 高 */
        ShadowMapResolution[ShadowMapResolution["HIGH"] = 2] = "HIGH";
        /** 非常高 */
        ShadowMapResolution[ShadowMapResolution["VERY_HIGH"] = 3] = "VERY_HIGH";
    })(ShadowMapResolution = FIREFLYX.ShadowMapResolution || (FIREFLYX.ShadowMapResolution = {}));
    /**
     * 灯光 视锥数据
     */
    class LightFrustumData {
        /**
         * 构造 灯光视锥数据
         * @param near 近平面
         * @param far 远平面
         * @param w 宽度
         * @param h 高度
         * @param position 位置
         * @param rotation 旋转
         */
        // eslint-disable-next-line no-useless-constructor
        constructor(near = 0, far = 0, w = 0, h = 0, position = new FIREFLYX.Vector3(), rotation = new FIREFLYX.Quaternion()) {
            this.near = near;
            this.far = far;
            this.w = w;
            this.h = h;
            this.position = position;
            this.rotation = rotation;
        }
    }
    FIREFLYX.LightFrustumData = LightFrustumData;
    /**
     * 光源组件
     */
    class Light extends FIREFLYX.Component {
        constructor() {
            super(...arguments);
            this._type = LightType.DIRECTIONAL;
            this._color = new FIREFLYX.Color4();
            this._intensity = 1;
            this._range = 10;
            this._spotAngle = 30;
            this._eventDisp = new FIREFLYX.EventDispatcher();
            this._viewProjMatrixList = [];
            /** 阴影类型 */
            this.shadowType = ShadowType.HARD_SHADOW;
            /** 阴影图分辨率 */
            this.shadowMapResolution = ShadowMapResolution.HIGH;
            /** 阴影图强度 0-1 */
            this.shadowStrength = 1;
            /** 阴影近平面 */
            this.shadowNear = 0.0;
            /** 基本偏移 */
            this.shadowBias = 0.05;
            /** 法线方向偏移 */
            this.shadowNormalBias = 0.4;
        }
        /** 光源类型 */
        get type() { return this._type; }
        set type(val) {
            if (this._type == val)
                return;
            this._type = val;
            if (this._eventDisp.listenerCount(`type_set`) > 0) {
                this._eventDisp.dispatch(`type_set`, this);
            }
        }
        /** 光源颜色 , rgb 分量 范围 0-1 ，a 分量为 HDR 强度*/
        get color() { return this._color; }
        set color(val) {
            this._color = val;
            if (this._eventDisp.listenerCount(`color_set`) > 0) {
                this._eventDisp.dispatch(`color_set`, this);
            }
        }
        /** 光源强度 */
        get intensity() { return this._intensity; }
        set intensity(val) {
            if (this._intensity == val)
                return;
            this._intensity = val;
            if (this._eventDisp.listenerCount(`intensity_set`) > 0) {
                this._eventDisp.dispatch(`intensity_set`, this);
            }
        }
        /** 光源光照范围 , Point、Spot 类型有效  */
        get range() { return this._range; }
        set range(val) {
            if (this._range == val)
                return;
            this._range = val;
            if (this._eventDisp.listenerCount(`range_set`) > 0) {
                this._eventDisp.dispatch(`range_set`, this);
            }
        }
        /** 聚光灯 角度值 */
        get spotAngle() { return this._spotAngle; }
        set spotAngle(val) {
            if (this._spotAngle == val)
                return;
            this._spotAngle = val;
            if (this._eventDisp.listenerCount(`spotAngle_set`) > 0) {
                this._eventDisp.dispatch(`spotAngle_set`, this);
            }
        }
        /**
         * VP 矩阵
         * @param index 级联索引
         * @returns
         */
        getViewProjMatrix(index) {
            if (index < 0)
                return null;
            if (!this._viewProjMatrixList[index]) {
                this._viewProjMatrixList[index] = new FIREFLYX.Matrix();
            }
            return this._viewProjMatrixList[index];
        }
        /**
         * 计算 方向光视锥数据
         * @param camFrustum 相机视锥(世界空间中的8个顶点位置坐标)
         * @param lightWRot 方向光世界空间旋转
         * @param out 输出视锥数据
         */
        static calcDirLightFrustumData(camFrustum, lightWRot, out) {
            const max = Light.HELP_VEC3;
            max.set(-Number.POSITIVE_INFINITY, -Number.POSITIVE_INFINITY, -Number.POSITIVE_INFINITY);
            const min = Light.HELP_VEC3_1;
            min.set(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
            const tempV3 = Light.HELP_VEC3_2;
            const wRot = lightWRot;
            const light2WorldMat = Light.HELP_MAT;
            FIREFLYX.Matrix.ComposeToRef(Light.HELP_VEC3_ONE, wRot, Light.HELP_VEC3_ZERO, light2WorldMat);
            const lightViewMat = Light.HELP_MAT_1;
            light2WorldMat.invertToRef(lightViewMat);
            for (let i = 0, len = camFrustum.length; i < len; i++) {
                const pos = camFrustum[i];
                const nPos = tempV3;
                FIREFLYX.Vector3.TransformCoordinatesToRef(pos, lightViewMat, nPos);
                //筛选 near far
                if (nPos.z > max.z) {
                    max.z = nPos.z;
                }
                if (nPos.z < min.z) {
                    min.z = nPos.z;
                }
                //筛选 w h
                if (nPos.x > max.x) {
                    max.x = nPos.x;
                }
                if (nPos.x < min.x) {
                    min.x = nPos.x;
                }
                if (nPos.y > max.y) {
                    max.y = nPos.y;
                }
                if (nPos.y < min.y) {
                    min.y = nPos.y;
                }
            }
            const center = tempV3;
            min.addToRef(max, center);
            center.scaleToRef(0.5, center);
            const offset = center;
            offset.z = min.z;
            const near = 0;
            const far = max.z - min.z;
            let w = max.x - min.x;
            let h = max.y - min.y;
            //
            out.near = near;
            out.far = far;
            out.w = w;
            out.h = h;
            FIREFLYX.Vector3.TransformCoordinatesToRef(offset, light2WorldMat, out.position);
            out.rotation.copyFrom(wRot);
        }
        /**
         * 计算 方向光视锥 顶点数据
         * @param lfData 视锥数据
         * @param outFrustum 相机视锥(世界空间中的8个顶点位置坐标)
         */
        static calcDirLightFrustumPoints(lfData, outFrustum) {
            const halfW = lfData.w / 2;
            const halfH = lfData.h / 2;
            const wPos = lfData.position;
            const wRot = lfData.rotation;
            const near = lfData.near;
            const far = lfData.far;
            //不管缩放
            const light2WordMat = FIREFLYX.Matrix.Compose(new FIREFLYX.Vector3(1, 1, 1), wRot, wPos);
            //
            outFrustum[0].set(-halfW, halfH, near); //n_0
            outFrustum[1].set(halfW, halfH, near); //n_1
            outFrustum[2].set(halfW, -halfH, near); //n_2
            outFrustum[3].set(-halfW, -halfH, near); //n_3
            outFrustum[4].set(-halfW, halfH, far); //f_0
            outFrustum[5].set(halfW, halfH, far); //f_1
            outFrustum[6].set(halfW, -halfH, far); //f_2
            outFrustum[7].set(-halfW, -halfH, far); //f_3
            outFrustum.forEach((v) => {
                FIREFLYX.Vector3.TransformCoordinatesToRef(v, light2WordMat, v);
            });
        }
        /**
         * 设置平行投影 ViewProjection 矩阵
         * @param index matrix 索引
         * @param orthoW 正交宽度
         * @param orthoH 正交高度度
         * @param far 远平面
         * @param position 起始位置
         */
        setVPMatrix(index, orthoW, orthoH, far, position) {
            if (index < 0 || !this.entity || !position)
                return;
            const viewMat = Light.HELP_MAT;
            const projMat = Light.HELP_MAT_1;
            const pos = position;
            const rot = this.entity.transform.rotation;
            //view mat
            FIREFLYX.Matrix.ComposeToRef(Light.HELP_VEC3_ONE, rot, pos, viewMat);
            viewMat.invertToRef(viewMat);
            //p mat
            FIREFLYX.Matrix.OrthoLHToRef(orthoW, orthoH, this.shadowNear, far, projMat);
            viewMat.multiplyToRef(projMat, this.getViewProjMatrix(index));
        }
        dispose() {
            super.dispose();
            if (this._eventDisp) {
                this._eventDisp.offAll();
            }
            this._eventDisp = null;
        }
        addListener(eventType, listener, thisArg) {
            this._eventDisp.on(eventType, listener, thisArg);
        }
        removeListener(eventType, listener, thisArg) {
            this._eventDisp.off(eventType, listener, thisArg);
        }
    }
    Light.HELP_MAT = new FIREFLYX.Matrix();
    Light.HELP_MAT_1 = new FIREFLYX.Matrix();
    Light.HELP_VEC3_ONE = new FIREFLYX.Vector3(1, 1, 1);
    Light.HELP_VEC3_ZERO = new FIREFLYX.Vector3(0, 0, 0);
    Light.HELP_VEC3 = new FIREFLYX.Vector3();
    Light.HELP_VEC3_1 = new FIREFLYX.Vector3();
    Light.HELP_VEC3_2 = new FIREFLYX.Vector3();
    //标记为light
    Light._useSign = "light";
    FIREFLYX.Light = Light;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="../renderer.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** 模型网格渲染组件 */
    class MeshRenderer extends FIREFLYX.Renderer {
        getVertexDataCount() {
            if (!this.mesh)
                return 0;
            return this.mesh.primitives.length;
        }
        uploadVertexData(index, out) {
            if (!this.mesh || index >= this.mesh.primitives.length)
                return;
            //获取相应 vao
            const vao = this.mesh.VAO;
            if (!vao)
                return;
            const gl = FIREFLYX.App.webgl2;
            gl.bindVertexArray(vao); //启用vao
            if (out) {
                const p = this.mesh.primitives[index];
                out.lenght = p.length;
                out.bufferOffset = p.bufferStart;
                out.elementType = this.mesh.elementType;
                out.drawMode = p.mode;
                out.frontFace = p.frontFace;
                out.material = p.material;
            }
        }
        dispose() {
            super.dispose();
            this.mesh = null;
        }
    }
    FIREFLYX.MeshRenderer = MeshRenderer;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 事件派发器 */
    class EventDispatcher {
        constructor() {
            this.events = {};
        }
        /**
         * 监听事件添加
         * @param eventType 事件类型
         * @param _cfun 事件触发回调方法 (Warn: 不要使用 func.bind() , 它会导致相等判断失败)
         * @param caller 回调方法执行者
         */
        on(eventType, _cfun, caller) {
            let eArr = this.events[eventType];
            let tempft = null;
            if (!eArr) {
                eArr = this.events[eventType] = [];
            }
            else {
                for (let ft of eArr) {
                    if (ft.cfun == _cfun) {
                        tempft = ft;
                        break;
                    }
                }
            }
            if (!tempft) {
                eArr.push({ cfun: _cfun, callers: [caller] });
            }
            else {
                let idx = tempft.callers.lastIndexOf(caller);
                if (idx == -1) {
                    tempft.callers.push(caller);
                }
            }
        }
        /**
         * 使用 EventDispatcher 对象注册指定类型，响应一次后自动移除。
         */
        Once() {
            return null;
        }
        /**
         * 发出事件
         * @param eventType 事件类型
         * @param args 传递参数
         * @returns 如果有侦听者则值为 true，否则值为 false。
         */
        dispatch(eventType, ...args) {
            let arr = this.events[eventType];
            if (!arr) {
                return false;
            }
            for (let fT of arr) {
                for (let thisArg of fT.callers) {
                    fT.cfun.apply(thisArg, args);
                }
            }
            return true;
        }
        /**
         * 移除事件监听者
         * @param eventType 事件类型
         * @param cFun 事件触发回调方法
         * @param caller 回调方法执行者
         */
        off(eventType, cFun, caller) {
            let arr = this.events[eventType];
            if (!arr) {
                return;
            }
            for (let i = 0, len = arr.length; i < len; ++i) {
                if (cFun == arr[i].cfun) {
                    let idx = arr[i].callers.lastIndexOf(caller);
                    if (idx != -1) {
                        arr[i].callers.splice(idx, 1);
                        if (arr[i].callers.length < 1) {
                            arr.splice(i, 1);
                        }
                        if (arr.length < 1) {
                            delete this.events[eventType];
                        }
                        break;
                    }
                }
            }
        }
        /**
         * 移除所有监听者
         */
        offAll() {
            this.events = {};
        }
        /**
         * 指定事件监听者的数量
         */
        listenerCount(eventType) {
            return this.events[eventType] ? this.events[eventType].length : 0;
        }
    }
    FIREFLYX.EventDispatcher = EventDispatcher;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class GlobalEventManager {
        constructor() {
            this._eventDisp = new FIREFLYX.EventDispatcher();
        }
        static get Instance() {
            if (!this._instance)
                this._instance = new GlobalEventManager();
            return this._instance;
        }
        dispatchEvent(eventType, ev) {
            return this._eventDisp.dispatch(eventType, ev);
        }
        addListener(eventType, listener, thisArg) {
            this._eventDisp.on(eventType, listener, thisArg);
        }
        removeListener(eventType, listener, thisArg) {
            this._eventDisp.off(eventType, listener, thisArg);
        }
        listenerCount(eventType) {
            return this._eventDisp.listenerCount(eventType);
        }
    }
    FIREFLYX.GlobalEventManager = GlobalEventManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class InputManager {
        static init() {
            if (this._isInited) {
                return;
            }
            FIREFLYX.MouseManager.instance.init(this._listeners);
            FIREFLYX.KeyboardManager.instance.init(this._listeners);
            FIREFLYX.TouchManager.instance.init(this._listeners);
            FIREFLYX.PointManager.instance.init();
            const canvas = FIREFLYX.App.canvasElement;
            this.attach(canvas);
            canvas.focus();
            canvas.addEventListener(`contextmenu`, (event) => event.preventDefault()); //关闭canvas 的右键菜单
            this._isInited = true;
        }
        static attach(element) {
            if (this._element) {
                this.detach();
            }
            this._element = element;
            this._listeners.forEach((listener) => {
                if (listener && element) {
                    element.addEventListener(listener[0], listener[1], true); //reg
                }
            });
        }
        static detach() {
            let element = this._element;
            if (!element) {
                return;
            }
            this._listeners.forEach((listener) => {
                if (listener && element) {
                    element.removeEventListener(listener[0], listener[1], false); //unreg
                }
            });
            this._element = null;
        }
    }
    InputManager._listeners = [];
    InputManager._isInited = false;
    FIREFLYX.InputManager = InputManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 按键管理器 */
    class KeyboardManager {
        init(list) {
            list.push(["keydown", this._onkeyDown.bind(this)]);
            list.push(["keypress", this._onkeyDown.bind(this)]);
            list.push(["keyup", this._onkeyUp.bind(this)]);
        }
        _onkeyDown(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("keydown", e);
        }
        _onkeyUp(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("keyup", e);
        }
    }
    KeyboardManager.instance = new KeyboardManager();
    FIREFLYX.KeyboardManager = KeyboardManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 鼠标管理器 */
    class MouseManager {
        init(list) {
            list.push(["mousedown", this._mouseDown.bind(this)]);
            list.push(["mouseup", this._mouseUp.bind(this)]);
            list.push(["mousemove", this._mouseMove.bind(this)]);
            list.push(["mousewheel", this._mouseWheel.bind(this)]);
            list.push(["DOMMouseScroll", this._mouseWheel.bind(this)]);
        }
        _mouseDown(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("mousedown", e);
        }
        _mouseUp(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("mouseup", e);
        }
        _mouseMove(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("mousemove", e);
        }
        _mouseWheel(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("mousewheel", e);
        }
    }
    MouseManager.instance = new MouseManager();
    FIREFLYX.MouseManager = MouseManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 点击输入管理器 , 统一 mouse 、touch 的单点输入事件
     */
    class PointManager {
        constructor() {
            this._clickLimit = 1.5; //click的 move 限制
            this._point = new FIREFLYX.Vector2();
            this._downPoint = new FIREFLYX.Vector2();
            this._isDown = false;
            this._touchID = -1;
        }
        init() {
            //绑定事件
            const eMgr = FIREFLYX.GlobalEventManager.Instance;
            //reg event
            eMgr.addListener("mousedown", (e) => { this.pointDown(e.x, e.y); }, this);
            eMgr.addListener("mouseup", (e) => { this.pointUp(e.x, e.y); }, this);
            eMgr.addListener("mousemove", (e) => { this.pointMove(e.x, e.y); }, this);
            eMgr.addListener("touchstart", this.onTouchstart, this);
            eMgr.addListener("touchend", this.onTouchend, this);
            eMgr.addListener("touchcancel", this.onTouchend, this);
            eMgr.addListener("touchmove", this.onTouchmove, this);
        }
        onTouchmove(e) {
            const touch = e.changedTouches[0];
            if (this._touchID == -1 || this._touchID != touch.identifier)
                return;
            this.pointMove(touch.clientX, touch.clientY);
        }
        onTouchstart(e) {
            const touch = e.changedTouches[0];
            if (this._touchID != -1 && this._touchID == touch.identifier)
                return;
            this._touchID = touch.identifier;
            this.pointDown(touch.clientX, touch.clientY);
        }
        onTouchend(e) {
            const touch = e.changedTouches[0];
            if (this._touchID == -1 || this._touchID != touch.identifier)
                return;
            this._touchID = -1;
            this.pointUp(touch.clientX, touch.clientY);
        }
        pointMove(x, y) {
            this._point.set(x, y);
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("pointMove", this._point);
        }
        pointUp(x, y) {
            if (!this._isDown)
                return;
            this._isDown = false;
            this._point.set(x, y);
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("pointUp", this._point);
            //检查click
            const isClick = Math.abs(this._downPoint.x - x) < this._clickLimit || Math.abs(this._downPoint.y - y) < this._clickLimit;
            if (isClick) {
                FIREFLYX.GlobalEventManager.Instance.dispatchEvent("pointClick", this._point);
            }
        }
        pointDown(x, y) {
            if (this._isDown)
                return;
            this._point.set(x, y);
            this._downPoint.set(x, y);
            this._isDown = true;
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("pointDown", this._point);
        }
    }
    PointManager.instance = new PointManager();
    FIREFLYX.PointManager = PointManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 触摸输入管理器 */
    class TouchManager {
        init(list) {
            list.push(["touchstart", this._touchStart.bind(this)]);
            list.push(["touchmove", this._touchMove.bind(this)]);
            list.push(["touchend", this._touchEnd.bind(this)]);
            list.push(["touchcancel", this._touchCancel.bind(this)]);
        }
        _touchStart(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("touchstart", e);
        }
        _touchMove(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("touchmove", e);
        }
        _touchEnd(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("touchend", e);
        }
        _touchCancel(e) {
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("touchcancel", e);
        }
    }
    TouchManager.instance = new TouchManager();
    FIREFLYX.TouchManager = TouchManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * Scalar class
     */
    class Scalar {
        /**
         * Boolean : true if the absolute difference between a and b is lower than epsilon (default = 1.401298E-45)
         */
        static WithinEpsilon(a, b, epsilon = 1.401298E-45) {
            let num = a - b;
            return -epsilon <= num && num <= epsilon;
        }
        /**
         * Returns a string : the upper case translation of the number i to hexadecimal.
         */
        static ToHex(i) {
            let str = i.toString(16);
            if (i <= 15) {
                return ("0" + str).toUpperCase();
            }
            return str.toUpperCase();
        }
        /**
         * Returns -1 if value is negative and +1 is value is positive.
         * Returns the value itself if it's equal to zero.
         */
        static Sign(value) {
            value = +value; // convert to a number
            if (value === 0 || isNaN(value))
                return value;
            return value > 0 ? 1 : -1;
        }
        /**
         * Returns the value itself if it's between min and max.
         * Returns min if the value is lower than min.
         * Returns max if the value is greater than max.
         */
        static Clamp(value, min = 0, max = 1) {
            return Math.min(max, Math.max(min, value));
        }
        /**
         * Returns the log2 of value.
         */
        static Log2(value) {
            return Math.log(value) * Math.LOG2E;
        }
        /**
        * Loops the value, so that it is never larger than length and never smaller than 0.
        *
        * This is similar to the modulo operator but it works with floating point numbers.
        * For example, using 3.0 for t and 2.5 for length, the result would be 0.5.
        * With t = 5 and length = 2.5, the result would be 0.0.
        * Note, however, that the behaviour is not defined for negative numbers as it is for the modulo operator
        */
        static Repeat(value, length) {
            return value - Math.floor(value / length) * length;
        }
        /**
        * Normalize the value between 0.0 and 1.0 using min and max values
        */
        static Normalize(value, min, max) {
            return (value - min) / (max - min);
        }
        /**
        * Denormalize the value from 0.0 and 1.0 using min and max values
        */
        static Denormalize(normalized, min, max) {
            return (normalized * (max - min) + min);
        }
        /**
        * Calculates the shortest difference between two given angles given in degrees.
        */
        static DeltaAngle(current, target) {
            let num = Scalar.Repeat(target - current, 360.0);
            if (num > 180.0) {
                num -= 360.0;
            }
            return num;
        }
        /**
        * PingPongs the value t, so that it is never larger than length and never smaller than 0.
        *
        * The returned value will move back and forth between 0 and length
        */
        static PingPong(tx, length) {
            let t = Scalar.Repeat(tx, length * 2.0);
            return length - Math.abs(t - length);
        }
        /**
        * Interpolates between min and max with smoothing at the limits.
        *
        * This function interpolates between min and max in a similar way to Lerp. However, the interpolation will gradually speed up
        * from the start and slow down toward the end. This is useful for creating natural-looking animation, fading and other transitions.
        */
        static SmoothStep(from, to, tx) {
            let t = Scalar.Clamp(tx);
            t = -2.0 * t * t * t + 3.0 * t * t;
            return to * t + from * (1.0 - t);
        }
        /**
        * Moves a value current towards target.
        *
        * This is essentially the same as Mathf.Lerp but instead the function will ensure that the speed never exceeds maxDelta.
        * Negative values of maxDelta pushes the value away from target.
        */
        static MoveTowards(current, target, maxDelta) {
            let result = 0;
            if (Math.abs(target - current) <= maxDelta) {
                result = target;
            }
            else {
                result = current + Scalar.Sign(target - current) * maxDelta;
            }
            return result;
        }
        /**
        * Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
        *
        * Variables current and target are assumed to be in degrees. For optimization reasons, negative values of maxDelta
        *  are not supported and may cause oscillation. To push current away from a target angle, add 180 to that angle instead.
        */
        static MoveTowardsAngle(current, target, maxDelta) {
            let num = Scalar.DeltaAngle(current, target);
            let result = 0;
            if (-maxDelta < num && num < maxDelta) {
                result = target;
            }
            else {
                target = current + num;
                result = Scalar.MoveTowards(current, target, maxDelta);
            }
            return result;
        }
        /**
            * Creates a new scalar with values linearly interpolated of "amount" between the start scalar and the end scalar.
            */
        static Lerp(start, end, amount) {
            return start + ((end - start) * amount);
        }
        /**
        * Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
        * The parameter t is clamped to the range [0, 1]. Variables a and b are assumed to be in degrees.
        */
        static LerpAngle(start, end, amount) {
            let num = Scalar.Repeat(end - start, 360.0);
            if (num > 180.0) {
                num -= 360.0;
            }
            return start + num * Scalar.Clamp(amount);
        }
        /**
        * Calculates the linear parameter t that produces the interpolant value within the range [a, b].
        */
        static InverseLerp(a, b, value) {
            let result = 0;
            if (a != b) {
                result = Scalar.Clamp((value - a) / (b - a));
            }
            else {
                result = 0.0;
            }
            return result;
        }
        /**
         * Returns a new scalar located for "amount" (float) on the Hermite spline defined by the scalars "value1", "value3", "tangent1", "tangent2".
         */
        static Hermite(value1, tangent1, value2, tangent2, amount) {
            let squared = amount * amount;
            let cubed = amount * squared;
            let part1 = ((2.0 * cubed) - (3.0 * squared)) + 1.0;
            let part2 = (-2.0 * cubed) + (3.0 * squared);
            let part3 = (cubed - (2.0 * squared)) + amount;
            let part4 = cubed - squared;
            return (((value1 * part1) + (value2 * part2)) + (tangent1 * part3)) + (tangent2 * part4);
        }
        /**
        * Returns a random float number between and min and max values
        */
        static RandomRange(min, max) {
            if (min === max)
                return min;
            return ((Math.random() * (max - min)) + min);
        }
        /**
        * This function returns percentage of a number in a given range.
        *
        * RangeToPercent(40,20,60) will return 0.5 (50%)
        * RangeToPercent(34,0,100) will return 0.34 (34%)
        */
        static RangeToPercent(number, min, max) {
            return ((number - min) / (max - min));
        }
        /**
        * This function returns number that corresponds to the percentage in a given range.
        *
        * PercentToRange(0.34,0,100) will return 34.
        */
        static PercentToRange(percent, min, max) {
            return ((max - min) * percent + min);
        }
        /**
         * Returns the angle converted to equivalent value between -Math.PI and Math.PI radians.
         * @param angle The angle to normalize in radian.
         * @return The converted angle.
         */
        static NormalizeRadians(angle) {
            // More precise but slower version kept for reference.
            // angle = angle % Tools.TwoPi;
            // angle = (angle + Tools.TwoPi) % Tools.TwoPi;
            //if (angle > Math.PI) {
            //	angle -= Tools.TwoPi;
            //}
            angle -= (Scalar.TwoPi * Math.floor((angle + Math.PI) / Scalar.TwoPi));
            return angle;
        }
    }
    /**
     * Two pi constants convenient for computation.
     */
    Scalar.TwoPi = Math.PI * 2;
    FIREFLYX.Scalar = Scalar;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 引擎全局入口对象
     */
    class App {
        /** 源 HTMLCanvasElement 对象 */
        static get canvasElement() { return FIREFLYX.ViewAspectManager.canvasElement; }
        /** WebGL2RenderingContext 对象 */
        static get webgl2() { return this._webgl2; }
        /** 上一帧的时间增量 */
        static get deltaTime() { return this._deltaTime; }
        /** 上一帧的时间增量 (不受 timeScale影响)*/
        static get unscaleDeltaTime() { return this._unscaleDeltaTime; }
        /** 启动后累计时间 */
        static get time() { return this._time; }
        /** 启动后累计时间 (不受 timeScale影响)*/
        static get unscaleTime() { return this._unscaleTime; }
        static init(element, webglOpt) {
            FIREFLYX.ViewAspectManager.init(element);
            this._webgl2 = this.canvasElement.getContext("webgl2", webglOpt) || {};
            //各系统初始化
            FIREFLYX.WebglCapability.init(this._webgl2);
            FIREFLYX.Browser.init();
            FIREFLYX.SceneManager.init(this._webgl2);
            FIREFLYX.InputManager.init();
            FIREFLYX.AssetManager.init();
            FIREFLYX.Graphics.init();
            //启动 update流
            requestAnimationFrame(this.loop);
        }
        static loop(timeStamp) {
            App._unscaleDeltaTime = (timeStamp - App._lastTimeStamp) * 0.001;
            App._unscaleTime += App._unscaleDeltaTime;
            App._deltaTime = App._unscaleDeltaTime * App._timeScale;
            App._time += App._deltaTime;
            App._lastTimeStamp = timeStamp;
            App.update(App._deltaTime);
            //loop
            requestAnimationFrame(App.loop);
        }
        static update(dt) {
            //派送事件
            const gem = FIREFLYX.GlobalEventManager.Instance;
            if (gem.listenerCount("appLoopStart") > 0)
                FIREFLYX.GlobalEventManager.Instance.dispatchEvent("appLoopStart", dt);
            //
            FIREFLYX.ViewAspectManager.update();
            //场景 update
            FIREFLYX.SceneManager.update(dt);
            //
            if (gem.listenerCount("appLoopEnd") > 0)
                FIREFLYX.GlobalEventManager.Instance.dispatchEvent("appLoopEnd", dt);
        }
    }
    App.version = "0.1.0";
    App._lastTimeStamp = 0;
    App._timeScale = 1;
    App._time = 0;
    App._deltaTime = 0;
    App._unscaleTime = 0;
    App._unscaleDeltaTime = 0;
    FIREFLYX.App = App;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 引擎全局入口对象
     */
    class Browser {
        static init() {
            return null;
        }
    }
    FIREFLYX.Browser = Browser;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 灯光收集器
     */
    class LightCollector {
        constructor() {
            //需要更新数据
            this._needUpdateUbo = false;
            //灯ID map
            this._lightIndexMap = {};
            this._addLights = [];
            this._collectedLights = [];
            const addN = 4 * LightCollector.MAX_ADD_LIGHT_COUNT;
            //array init
            this._mainLightDirF32 = new Float32Array(4);
            this._mainLightColorF32 = new Float32Array(4);
            this._addLightsPosF32 = new Float32Array(addN);
            this._addLightsColorF32 = new Float32Array(addN);
            this._addLightsAttF32 = new Float32Array(addN);
            this._addLightsSpotDirF32 = new Float32Array(addN);
            //init ubo
            const gl = FIREFLYX.App.webgl2;
            this._ubo = gl.createBuffer();
            //bind start
            gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
            //初始化 buffer 设置大小 4 = float
            const lihgtBlockSize = this._uboBlockSize = 4 * (4 + 4 + addN + addN + addN + addN);
            gl.bufferData(gl.UNIFORM_BUFFER, lihgtBlockSize, gl.DYNAMIC_DRAW);
            //bind end
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
        }
        /** 光源数据的 ubo对象 */
        get ubo() { return this._ubo; }
        /** ubo块byte占用大小 */
        get uboBlockSize() { return this._uboBlockSize; }
        /** 主光源 */
        get mainLight() { return this._mainLight; }
        /**
         * 启用灯光
         * @param light
         */
        enableLight(light) {
            if (!light || !light.entity || this._mainLight == light || this._collectedLights.indexOf(light) != -1)
                return;
            //是否是 mainLight
            if (light.type == FIREFLYX.LightType.DIRECTIONAL && this._mainLight == null) {
                this._mainLight = light;
                this._needUpdateUbo = true;
            }
            else { //是否能 加到附加light
                this._collectedLights.push(light);
                if (this._addLights.length < LightCollector.MAX_ADD_LIGHT_COUNT) {
                    this._lightIndexMap[light.getInstanceID()] = this._addLights.length; //记录索引到map
                    this._addLights.push(light);
                    this._needUpdateUbo = true;
                }
            }
            //监听enable的灯光
            light.addListener(`type_set`, this.onLightTypeSet, this);
            light.addListener(`color_set`, this.onLightColorIntensitySet, this);
            light.addListener(`intensity_set`, this.onLightColorIntensitySet, this);
            light.addListener(`range_set`, this.onLightRangeSet, this);
            light.addListener(`spotAngle_set`, this.onLightSpotAngleSet, this);
            light.entity.transform.addListener(`postion_set`, this.onLightPostionSet, this);
            light.entity.transform.addListener(`rotation_set`, this.onLightRotationSet, this);
        }
        /**
         * 关闭灯光
         * @param light
         */
        disableLight(light) {
            //
            if (!light || (!this._mainLight && this._collectedLights.length == 0))
                return;
            let cLights = this._collectedLights;
            let lights = this._addLights;
            let needUpdate = true;
            //是否是 mainLight
            if (light == this._mainLight) {
                this._mainLight = null;
                //add 中寻找下一个 main Light
                for (let i = 0, len = cLights.length; i < len; i++) {
                    const l = cLights[i];
                    if (l.type == FIREFLYX.LightType.DIRECTIONAL) {
                        this._mainLight = l;
                        break;
                    }
                }
            }
            else { //是否是 附加light
                let idx = cLights.indexOf(light);
                cLights.splice(idx, 1);
                idx = lights.indexOf(light);
                if (idx != -1) {
                    cLights.splice(idx, 1);
                    //将 _collectedLights 填入 _addLights
                    if (cLights.length >= LightCollector.MAX_ADD_LIGHT_COUNT) {
                        lights.length = cLights.length;
                        this._lightIndexMap = {};
                        cLights.forEach((v) => {
                            this._lightIndexMap[v.getInstanceID()] = lights.length;
                            lights.push(v);
                        });
                    }
                }
                else {
                    needUpdate = false; //只是 清理了 _collectedLights 中的数据
                }
            }
            this._needUpdateUbo = needUpdate;
            //取消监听disable的灯光
            light.removeListener(`type_set`, this.onLightTypeSet, this);
            light.removeListener(`color_set`, this.onLightColorIntensitySet, this);
            light.removeListener(`intensity_set`, this.onLightColorIntensitySet, this);
            light.removeListener(`range_set`, this.onLightRangeSet, this);
            light.removeListener(`spotAngle_set`, this.onLightSpotAngleSet, this);
            if (light.entity) {
                light.entity.transform.removeListener(`postion_set`, this.onLightPostionSet, this);
                light.entity.transform.removeListener(`rotation_set`, this.onLightRotationSet, this);
            }
        }
        /**
         * 刷新 ubo 的数据
         * @returns
         */
        refreshUboData() {
            if (!this._needUpdateUbo)
                return;
            this._needUpdateUbo = false;
            //mainLight
            const _ml = this._mainLight;
            const _mlDir = LightCollector.HELP_VEC_3;
            let _mlCR = 0, _mlCG = 0, _mlCB = 0;
            _mlDir.set(0, 0, 0);
            if (_ml) {
                //direction
                if (_ml.entity) {
                    _ml.entity.transform.getForwardToRef(_mlDir);
                }
                //color
                const mlI = _ml.intensity;
                _mlCR = _ml.color.r * mlI;
                _mlCG = _ml.color.g * mlI;
                _mlCB = _ml.color.b * mlI;
            }
            this._mainLightDirF32[0] = _mlDir.x;
            this._mainLightDirF32[1] = _mlDir.y;
            this._mainLightDirF32[2] = _mlDir.z;
            this._mainLightDirF32[3] = 0;
            this._mainLightColorF32[0] = _mlCR;
            this._mainLightColorF32[1] = _mlCG;
            this._mainLightColorF32[2] = _mlCB;
            this._mainLightColorF32[3] = 1;
            //additional lihgts
            for (let i = 0, len = this._addLights.length; i < len; i++) {
                //
                const sIdx = 4 * i;
                const _addl = this._addLights[i];
                let _cR = 0, _cG = 0, _cB = 0;
                let _posX = 0, _posY = 0, _posZ = 0;
                if (_addl) {
                    //direction
                    if (_addl.entity) {
                        const addlPos = _addl.entity.transform.position;
                        _posX = addlPos.x;
                        _posY = addlPos.y;
                        _posZ = addlPos.z;
                    }
                    //color
                    const addlI = _addl.intensity;
                    _cR = _addl.color.r * addlI;
                    _cG = _addl.color.g * addlI;
                    _cB = _addl.color.b * addlI;
                    //attenuation
                    //spotDir
                    switch (_addl.type) {
                        case FIREFLYX.LightType.POINT:
                            break;
                        case FIREFLYX.LightType.SPOT:
                            break;
                        default:
                    }
                }
                this._addLightsPosF32[sIdx + 0] = _posX;
                this._addLightsPosF32[sIdx + 1] = _posY;
                this._addLightsPosF32[sIdx + 2] = _posZ;
                this._addLightsPosF32[sIdx + 3] = 0;
                this._addLightsColorF32[sIdx + 0] = _cR;
                this._addLightsColorF32[sIdx + 1] = _cG;
                this._addLightsColorF32[sIdx + 2] = _cB;
                this._addLightsColorF32[sIdx + 3] = 1;
            }
            const addByteN = 4 * LightCollector.MAX_ADD_LIGHT_COUNT * 4;
            const gl = FIREFLYX.App.webgl2;
            //ubo bind start
            gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
            //set data
            let bOffset = 0;
            gl.bufferSubData(gl.UNIFORM_BUFFER, bOffset, this._mainLightDirF32);
            bOffset += 4 * 4;
            gl.bufferSubData(gl.UNIFORM_BUFFER, bOffset, this._mainLightColorF32);
            bOffset += 4 * 4;
            gl.bufferSubData(gl.UNIFORM_BUFFER, bOffset, this._addLightsPosF32);
            bOffset += addByteN;
            gl.bufferSubData(gl.UNIFORM_BUFFER, bOffset, this._addLightsColorF32);
            bOffset += addByteN;
            gl.bufferSubData(gl.UNIFORM_BUFFER, bOffset, this._addLightsAttF32);
            bOffset += addByteN;
            gl.bufferSubData(gl.UNIFORM_BUFFER, bOffset, this._addLightsSpotDirF32);
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
        }
        /**
         * 设置 灯光 颜色 和 强度 到 UBO
         * @param light 灯光
         * @returns
         */
        setColorIntensity2Ubo(light) {
            if (!light)
                return;
            //ubo
            const gl = FIREFLYX.App.webgl2;
            gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
            if (light == this._mainLight) {
                const _color = this._mainLight.color;
                const _intensity = this._mainLight.intensity;
                this._mainLightColorF32[0] = _color.r * _intensity;
                this._mainLightColorF32[1] = _color.g * _intensity;
                this._mainLightColorF32[2] = _color.b * _intensity;
                this._mainLightColorF32[3] = 1;
                //set ubo buffer
                gl.bufferSubData(gl.UNIFORM_BUFFER, 4 * 4, this._mainLightColorF32);
            }
            else {
                const lIndex = this._lightIndexMap[light.getInstanceID()];
                if (lIndex == null) {
                    const _addl = this._addLights[lIndex];
                    const _color = _addl.color;
                    const _intensity = _addl.intensity;
                    const sIdx = 4 * lIndex;
                    this._addLightsColorF32[sIdx + 0] = _color.r * _intensity;
                    this._addLightsColorF32[sIdx + 1] = _color.g * _intensity;
                    this._addLightsColorF32[sIdx + 2] = _color.b * _intensity;
                    this._addLightsColorF32[sIdx + 3] = 1;
                    const addByteN = 4 * LightCollector.MAX_ADD_LIGHT_COUNT * 4;
                    //set ubo buffer
                    gl.bufferSubData(gl.UNIFORM_BUFFER, addByteN, this._addLightsColorF32);
                }
            }
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
        }
        /**
         * 设置 灯光 方向 到 UBO
         * @param light 灯光
         * @returns
         */
        setDirection2Ubo(light) {
            const e = light.entity;
            if (!light || !e || light.type == FIREFLYX.LightType.POINT)
                return;
            //ubo
            const gl = FIREFLYX.App.webgl2;
            if (light == this._mainLight) {
                const _mlDir = LightCollector.HELP_VEC_3;
                e.transform.getForwardToRef(_mlDir);
                this._mainLightDirF32[0] = _mlDir.x;
                this._mainLightDirF32[1] = _mlDir.y;
                this._mainLightDirF32[2] = _mlDir.z;
                this._mainLightDirF32[3] = 0;
                gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
                //set to ubo buffer
                gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this._mainLightDirF32);
            }
            else {
                switch (light.type) {
                    case FIREFLYX.LightType.DIRECTIONAL:
                        break;
                    case FIREFLYX.LightType.SPOT:
                        break;
                    default:
                }
            }
            //ubo bind null
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
        }
        onLightTypeSet(light) {
            if (!light)
                return;
            if (light == this._mainLight) {
                if (light.type != FIREFLYX.LightType.DIRECTIONAL) {
                    this._needUpdateUbo = true;
                }
            }
            else {
                switch (light.type) {
                    case FIREFLYX.LightType.DIRECTIONAL:
                        break;
                    case FIREFLYX.LightType.POINT:
                        break;
                    case FIREFLYX.LightType.SPOT:
                        break;
                    default:
                }
            }
        }
        /** 当 灯光 颜色 和 强度 有变化 */
        onLightColorIntensitySet(light) {
            this.setColorIntensity2Ubo(light);
        }
        onLightRangeSet(light) {
            if (!light || light.type == FIREFLYX.LightType.DIRECTIONAL)
                return;
        }
        onLightSpotAngleSet(light) {
            if (!light || light.type != FIREFLYX.LightType.SPOT)
                return;
        }
        onLightPostionSet(transform) {
            const e = transform.entity;
            if (!e)
                return;
            const light = e.light;
            if (!light || light.type == FIREFLYX.LightType.DIRECTIONAL)
                return;
        }
        onLightRotationSet(transform) {
            const e = transform.entity;
            if (!e || !e.light)
                return;
            this.setDirection2Ubo(e.light);
        }
    }
    /** 能收集 的附加最大灯光数量 */
    LightCollector.MAX_ADD_LIGHT_COUNT = 64;
    LightCollector.HELP_VEC_3 = new FIREFLYX.Vector3();
    FIREFLYX.LightCollector = LightCollector;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 渲染管线父类 */
    class RenderPipeline {
        constructor() {
            this._cullPassRendererIDs = [];
        }
        /**
         * 执行渲染
         * @param context 引擎渲染上下文对象
         * @param cameras 相机列表
         */
        render(context, cameras) {
            //
        }
        /**
         * 相机视锥剔除
         * @param camera 相机
         * @param rendererMap  渲染器容器
         * @param result 通过剔除的列表
         */
        cameraFrustumCulling(camera, rendererMap, result) {
            //
            result.length = 0;
            rendererMap.forEach((v, k) => {
                //用相机检测是否 通过
                let isPass = true;
                if (isPass) {
                    result.push(k);
                }
            });
        }
    }
    /** 光照模式 总是渲染, 不启用光照 */
    RenderPipeline.LM_ALWAYS = "Always";
    /** 光照模式 前向光照渲染 */
    RenderPipeline.LM_FORWARD = "Forward";
    /** 光照模式 延迟光照渲染 */
    RenderPipeline.LM_DEFERRED = "Deferred";
    /** 光照模式  阴影投射*/
    RenderPipeline.LM_SHADOW_CASTER = "ShadowCaster";
    FIREFLYX.RenderPipeline = RenderPipeline;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** ubo 绑定 索引枚举 */
    let UBOBindingEnum;
    (function (UBOBindingEnum) {
        /** 相机设置 */
        UBOBindingEnum[UBOBindingEnum["CAMERA"] = 0] = "CAMERA";
        /** 模型设置 */
        UBOBindingEnum[UBOBindingEnum["MODEL"] = 1] = "MODEL";
        /** 灯光设置 */
        UBOBindingEnum[UBOBindingEnum["LIGHT"] = 2] = "LIGHT";
        /** 材质设置 开始 */
        UBOBindingEnum[UBOBindingEnum["MATERIAL_START"] = 3] = "MATERIAL_START";
    })(UBOBindingEnum = FIREFLYX.UBOBindingEnum || (FIREFLYX.UBOBindingEnum = {}));
    /** 阴影级联数 */
    let ShadowCascade;
    (function (ShadowCascade) {
        /** 一个 */
        ShadowCascade[ShadowCascade["ONE"] = 1] = "ONE";
        /** 二个 */
        ShadowCascade[ShadowCascade["TWO"] = 2] = "TWO";
        /** 四个 */
        ShadowCascade[ShadowCascade["FOUR"] = 4] = "FOUR";
    })(ShadowCascade = FIREFLYX.ShadowCascade || (FIREFLYX.ShadowCascade = {}));
    /** 引擎渲染上下文对象 */
    class RenderContext {
        constructor(gl) {
            this._ignoreInMaterialMap = {};
            this._ignoreInMaterialBlockMap = {};
            this._shadowCascades = ShadowCascade.FOUR;
            this._shadowCascadeSplitsArr = [
                [],
                [0.333],
                [0.143, 0.428],
                [0.067, 0.2, 0.467]
            ];
            this._curCamPixelViewPort = new FIREFLYX.Rect(0, 0, 100, 100);
            this._camViewProjMatrix = new FIREFLYX.Matrix();
            this._builtinUniformMap = new Map();
            /** 天空盒材质 */
            this.skyboxMaterial = null;
            /** 光照 周围环境关颜色 ，w分量为 hdr 强度*/
            this.lightingAmbient = new FIREFLYX.Color4(0.15, 0.22, 0.27, 0);
            /** 阴影绘制距离 */
            this.shadowDistance = 150;
            /** 软阴影是方差阴影 */
            this.isVSM = true;
            /** 方差阴影采样次数 */
            this.vsmSampleCount = 8;
            /** 方差阴影采样范围 */
            this.vsmSampleRadius = 3;
            this._gl = gl;
            this._texActivator = new FIREFLYX.TextureActivator(gl);
            this._unifUseInfoMap = this.makeUnifUseInfoMap();
        }
        /** 阴影级联数 范围 1-4 */
        get shadowCascades() { return this._shadowCascades; }
        set shadowCascades(val) {
            if (val == this._shadowCascades)
                return;
            val = val < 1 ? 1 : val > 4 ? 4 : val;
            this._shadowCascades = val;
        }
        /** 当前的相机 视口大小数据 */
        get curCamPixelViewPort() { return this._curCamPixelViewPort; }
        /** 获取相机View Projection 矩阵 */
        get camViewProjMatrix() { return this._camViewProjMatrix; }
        /** 引擎内建uniform变量 */
        get builtinUniformMap() { return this._builtinUniformMap; }
        /**
         * 获取指定索引 阴影级联拆划分值
         * @param splitIndex 分段索引
         * @returns 有效值为 0 - 1
         */
        getShadowCascadeSplit(splitIndex) {
            if (isNaN(splitIndex) || splitIndex < 0)
                return -1;
            const splits = this._shadowCascadeSplitsArr[this._shadowCascades - 1];
            if (splitIndex >= splits.length)
                return 1;
            return splits[splitIndex];
        }
        /**
         * 设置指定索引 阴影级联拆划分值
         * @param splitIndex 分段索引
         * @param val 值
         */
        setShadowCascadeSplit(splitIndex, val) {
            if (isNaN(splitIndex) || splitIndex < 0)
                return;
            const splits = this._shadowCascadeSplitsArr[this._shadowCascades];
            if (splitIndex >= splits.length)
                return;
            const last = splitIndex == 0 || splits.length < 2 ? 0 : splits[splitIndex - 1];
            const next = splitIndex == splits.length - 1 || splits.length < 2 ? 1 : splits[splitIndex + 1];
            splits[splitIndex] = val < last ? last : val > next ? next : val;
        }
        /** webgl渲染上下文 */
        get webgl2() { return this._gl; }
        /** webgl纹理 激活器  */
        get textrueActivator() { return this._texActivator; }
        /** 在材质中忽略 的uniform 字典 */
        get ignoreInMaterialMap() { return this._ignoreInMaterialMap; }
        /** 在材质中忽略 的 uniformBlock 字典 */
        get ignoreInMaterialBlockMap() { return this._ignoreInMaterialBlockMap; }
        /** unifrom 使用信息 字典 */
        get uniformUseInfoMap() { return this._unifUseInfoMap; }
        /** 创建uniform 类型字典 */
        makeUnifUseInfoMap() {
            const gl = this.webgl2;
            let _map = {};
            _map[gl.FLOAT] = { lenght: 1, align: 1, arrType: Float32Array, uploadFun: gl.uniform1fv.bind(gl), defVal: 0 };
            _map[gl.FLOAT_VEC2] = { lenght: 2, align: 2, arrType: Float32Array, uploadFun: gl.uniform2fv.bind(gl), defVal: new Float32Array(2) };
            _map[gl.FLOAT_VEC3] = { lenght: 3, align: 4, arrType: Float32Array, uploadFun: gl.uniform3fv.bind(gl), defVal: new Float32Array(3) };
            _map[gl.FLOAT_VEC4] = { lenght: 4, align: 4, arrType: Float32Array, uploadFun: gl.uniform4fv.bind(gl), defVal: new Float32Array(4) };
            _map[gl.INT] = { lenght: 1, align: 1, arrType: Int32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: 0 };
            _map[gl.INT_VEC2] = { lenght: 2, align: 2, arrType: Int32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: new Int32Array(2) };
            _map[gl.INT_VEC3] = { lenght: 3, align: 4, arrType: Int32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: new Int32Array(2) };
            _map[gl.INT_VEC4] = { lenght: 4, align: 4, arrType: Int32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: new Int32Array(2) };
            _map[gl.BOOL] = { lenght: 1, align: 1, arrType: Uint32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: 0 };
            _map[gl.BOOL_VEC2] = { lenght: 2, align: 2, arrType: Uint32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: new Uint8Array(2) };
            _map[gl.BOOL_VEC3] = { lenght: 3, align: 4, arrType: Uint32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: new Uint8Array(3) };
            _map[gl.BOOL_VEC4] = { lenght: 4, align: 4, arrType: Uint32Array, uploadFun: gl.uniform1iv.bind(gl), defVal: new Uint8Array(4) };
            _map[gl.FLOAT_MAT2] = { lenght: 8, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix2fv.bind(gl), defVal: new Float32Array(4) };
            _map[gl.FLOAT_MAT3] = { lenght: 12, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix3fv.bind(gl), defVal: new Float32Array(9) };
            _map[gl.FLOAT_MAT4] = { lenght: 16, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix4fv.bind(gl), defVal: new Float32Array(16) };
            //webgl2 add
            _map[gl.UNSIGNED_INT] = { lenght: 1, align: 1, arrType: Uint32Array, uploadFun: gl.uniform1uiv.bind(gl), defVal: 0 };
            _map[gl.UNSIGNED_INT_VEC2] = { lenght: 2, align: 2, arrType: Uint32Array, uploadFun: gl.uniform2uiv.bind(gl), defVal: new Uint8Array(2) };
            _map[gl.UNSIGNED_INT_VEC3] = { lenght: 3, align: 4, arrType: Uint32Array, uploadFun: gl.uniform3uiv.bind(gl), defVal: new Uint8Array(3) };
            _map[gl.UNSIGNED_INT_VEC4] = { lenght: 4, align: 4, arrType: Uint32Array, uploadFun: gl.uniform4uiv.bind(gl), defVal: new Uint8Array(4) };
            _map[gl.FLOAT_MAT2x3] = { lenght: 8, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix2x3fv.bind(gl), defVal: new Float32Array(6) };
            _map[gl.FLOAT_MAT2x4] = { lenght: 8, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix2x4fv.bind(gl), defVal: new Float32Array(8) };
            _map[gl.FLOAT_MAT3x2] = { lenght: 12, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix3x2fv.bind(gl), defVal: new Float32Array(6) };
            _map[gl.FLOAT_MAT3x4] = { lenght: 12, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix3x4fv.bind(gl), defVal: new Float32Array(12) };
            _map[gl.FLOAT_MAT4x2] = { lenght: 16, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix4x2fv.bind(gl), defVal: new Float32Array(8) };
            _map[gl.FLOAT_MAT4x3] = { lenght: 16, align: 4, arrType: Float32Array, uploadFun: gl.uniformMatrix4x3fv.bind(gl), defVal: new Float32Array(12) };
            return _map;
        }
        /**
         * 设置ubo 子数据
         * @param offset 偏移
         * @param val 值
         * @param arrType 数组类型
         */
        uboSubDataSet(offset, val, arrType) {
            const gl = this.webgl2;
            let arr = null;
            if (val != null) {
                const arrTypeSame = val.byteLength != null && val instanceof (arrType); //判断当前 值对象 类型是否和 arrType 一致
                arr = arrTypeSame ? val : (new arrType(Array.isArray(val) ? val : [val]));
            }
            gl.bufferSubData(gl.UNIFORM_BUFFER, offset, arr);
        }
        /**
         * 设置 UBO 数据
         * @param useSP 使用的 WebGLProgram
         * @param uboBlockIndex ubo 块索引
         * @param valueMaps uniform值map 列表
         * @returns
         */
        uboDataSet(useSP, uboBlockIndex, ...valueMaps) {
            const gl = this.webgl2;
            //
            //从 block Parameter 获取信息 自动填充参数 使用默认的 std140 对齐模式
            //获取 block 字段数
            const lbSubIdxList = gl.getActiveUniformBlockParameter(useSP, uboBlockIndex, gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES);
            const valMaps = valueMaps;
            if (!valueMaps || valueMaps.length < 1) {
                console.error(`valueMaps is null of empty. `);
                return;
            }
            let alignOffset = 0; //对齐偏移
            let byteOffset = 0; //实际byte偏移
            for (let i = 0, len = lbSubIdxList.length; i < len; i++) {
                const lbSubIdx = lbSubIdxList[i];
                //获取unifrom 信息
                const unifromInfo = gl.getActiveUniform(useSP, lbSubIdx);
                if (!unifromInfo)
                    continue;
                const isArray = unifromInfo.size > 1; //当前字段是否是数组
                // let unifName = unifromInfo.name.substring(unifromInfo.name.lastIndexOf(".") + 1);
                let unifName = unifromInfo.name;
                if (isArray)
                    unifName = unifName.substring(0, unifName.length - 3);
                const vMapLen = valMaps.length;
                let vMapIdx = 0;
                let valMap = valMaps[vMapIdx];
                let val = valMap[unifName];
                //遍历 替补map
                while (val == null && vMapIdx < vMapLen) {
                    valMap = valMaps[vMapIdx];
                    vMapIdx++;
                    val = valMap[unifName];
                }
                if (val == null) {
                    //null 处理
                    if (isArray)
                        val = [];
                }
                const unifTypeInfo = this._unifUseInfoMap[unifromInfo.type];
                let byteSize;
                let alignSize;
                byteSize = unifTypeInfo.lenght * 4;
                if (!isArray) {
                    alignSize = unifTypeInfo.align * 4;
                    alignOffset = Math.ceil(byteOffset / alignSize) * alignSize;
                    //包装数据
                    this.uboSubDataSet(alignOffset, val, unifTypeInfo.arrType);
                    //
                    byteOffset = alignOffset + byteSize;
                }
                else if (Array.isArray(val)) { //数组模式，值对象也必须是数组
                    //数组模式，对齐 只考虑 4 * 4 = 16
                    alignSize = 16;
                    //实际 数组元素的单个内存占用 , 大于 16 是矩阵类型
                    const _arrByteSize = (unifTypeInfo.lenght < 4 ? 4 : unifTypeInfo.lenght) * 4;
                    for (let j = 0, len1 = unifromInfo.size; j < len1; j++) {
                        alignOffset = Math.ceil(byteOffset / alignSize) * alignSize;
                        const _val = val[j];
                        if (_val != null) {
                            this.uboSubDataSet(alignOffset, _val, unifTypeInfo.arrType);
                        }
                        else {
                            console.error(` ${unifName} value array index of ${j} is null. `);
                        }
                        byteOffset = alignOffset + _arrByteSize;
                    }
                }
                else {
                    console.error(` ${unifName} value is invalid , The array type field of the block element. The value entered must be an array object. `);
                }
            }
        }
    }
    /** 相机 ubo block名 */
    RenderContext.UBO_CAMERA_BLOCK_NAME = "ubo_cam_block";
    /** 模型 ubo block名 */
    RenderContext.UBO_MODEL_BLOCK_NAME = "ubo_model_block";
    /** 光照 ubo block名 */
    RenderContext.UBO_LIGHT_BLOCK_NAME = "ubo_light_block";
    /** 非透明渲染队列开始值 */
    RenderContext.RENDER_QUEUE_OPAQUE = 0;
    /** ALPHA测试渲染队列开始值 */
    RenderContext.RENDER_QUEUE_ALPHA_TEST = 2000;
    /** 透明渲染队列开始值 */
    RenderContext.RENDER_QUEUE_TRANSPARENT = 4000;
    FIREFLYX.RenderContext = RenderContext;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./renderPipeline.ts" />
/// <reference path="./renderContext.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** 渲染器队列数据 */
    class RenderQueueData {
        constructor() {
            this.rendererIDs = [];
            this.materialCounts = [];
            this.materials = [];
        }
        clear() {
            this.rendererIDs.length = 0;
            this.materialCounts.length = 0;
            this.materials.length = 0;
        }
    }
    /** 渲染器队列收集器 */
    class RenderQueueCollect {
        constructor() {
            this._dataMap = new Map();
            this._renderQueueIDs = [];
            this._rqGetOrder = [];
            this._rqDirty = false;
        }
        /** 渲染队列数据 字典容器 */
        get dataMap() { return this._dataMap; }
        /** 排序过的队列id */
        get renderQueueIDs() {
            const rqs = this._renderQueueIDs;
            if (this._rqDirty) {
                this._rqDirty = false;
                rqs.length = 0;
                this._dataMap.forEach((v, k) => { rqs.push(k); });
                rqs.sort();
            }
            return rqs;
        }
        /**
         * 清理
         */
        clear() {
            const _p = RenderQueueCollect.pool;
            const rqOrder = this._rqGetOrder;
            const len = rqOrder.length;
            //优化 对池的使用 ，反向循环遍历 delete
            for (let i = len - 1; i >= 0; i--) {
                const id = rqOrder[i];
                const data = this._dataMap.get(id);
                data.clear();
                _p.delete(data);
            }
            this._dataMap.clear();
            this._rqGetOrder.length = 0;
        }
        /**
         * 获取RenderQueueData
         * @param renderQueueID 指定ID
         * @returns RenderQueueData对象
         */
        getQueue(renderQueueID) {
            //
            if (!this._dataMap.has(renderQueueID)) {
                this._rqDirty = true;
                const rq = RenderQueueCollect.pool.new();
                this._dataMap.set(renderQueueID, rq);
                this._rqGetOrder.push(renderQueueID);
            }
            return this._dataMap.get(renderQueueID);
        }
    }
    RenderQueueCollect.pool = new FIREFLYX.Pool(RenderQueueData);
    class RendererCombData {
        constructor() {
            this.mtrStartIdx = 0;
            this.mtrCount = 0;
        }
        /** 材质组信息池对象 */
        static get pool() { return this._pool; }
        clear() {
            this.r = null;
            this.mtrStartIdx = 0;
            this.mtrCount = 0;
        }
    }
    RendererCombData._pool = new FIREFLYX.Pool(RendererCombData, 256, (obj) => { obj.clear(); });
    /** 材质组信息 */
    class MaterialGroupInfo {
        /** 材质组信息池对象 */
        static get pool() { return this._pool; }
        /** 清空, 方便重新使用*/
        clear() {
            this.mainMaterial = null;
            if (this.transparents)
                this.transparents.length = 0;
            if (this.shadowCasters)
                this.shadowCasters.length = 0;
        }
    }
    MaterialGroupInfo._pool = new FIREFLYX.Pool(MaterialGroupInfo, 256, (obj) => { obj.clear(); });
    /** 渲染对象的材质信息 */
    class RendererMaterialInfo {
        constructor() {
            /** 渲染器的ID值 */
            this.rendererID = NaN;
            /** 材质组信息列表 */
            this._mGInfos = [];
            /** 阴影投射材质组索引列表 */
            this.shadowCasterMGIndexs = [];
            /** 透明混合材质组索引列表 */
            this.transparentMGIndexs = [];
        }
        /** 材质组信息池对象 */
        static get pool() { return this._pool; }
        /** 材质组信息列表 长度 */
        get mGInfoLength() { return this._mGInfos.length; }
        /** 添加新的 材质组信息 到列表，并返回该对象 */
        addMGInfo() {
            const mgInfo = MaterialGroupInfo.pool.new();
            this._mGInfos.push(mgInfo);
            return mgInfo;
        }
        /** 获取 材质组信息 */
        getMGInfo(index) {
            let len = this._mGInfos.length;
            if (isNaN(index) || index < 0 || index >= len)
                return null;
            return this._mGInfos[index];
        }
        /** 清空, 方便重新使用*/
        clear() {
            this.rendererID = NaN;
            for (let i = 0, len = this._mGInfos.length; i < len; i++) {
                MaterialGroupInfo.pool.delete(this._mGInfos[i]);
            }
            this._mGInfos.length = 0;
            this.shadowCasterMGIndexs.length = 0;
            this.transparentMGIndexs.length = 0;
        }
    }
    RendererMaterialInfo._pool = new FIREFLYX.Pool(RendererMaterialInfo, 64, (obj) => { obj.clear(); });
    /**
     * 引擎内建 前向渲染 管线
     */
    class ForwardPipeline extends FIREFLYX.RenderPipeline {
        // eslint-disable-next-line no-useless-constructor
        constructor() {
            super();
            this.help_MtrArr = [];
            this.help_rdCombArr = [];
            this.help_RVDataResult = {
                material: 0,
                lenght: 0,
                bufferOffset: 0,
                elementType: -1,
                drawMode: WebGL2RenderingContext.TRIANGLES,
                frontFace: WebGL2RenderingContext.CCW
            };
            this.help_StrBoolMap = new Map();
            this._forwardRenderQueueC = new RenderQueueCollect();
            this._fullScreenViewPortRect = new FIREFLYX.Rect(0, 0, 1, 1);
            this._mainShadowMapList = [];
            this._mainVSMList = [];
            this._cameraFrustumPointsList = [];
            this._lightFrustumDataList = [];
            this._mainShadowCSMSplit = new Float32Array(4); //[split0,split1,split2,shadowDistance]
            this._resolution = new Float32Array(2);
            this._fboResolution = new Float32Array(2);
            this._shadowCasterRList = [];
            this._rendererMInfoMap = new Map();
            this._drawDebugFrame = ""; //渲染 调试帧类型(为空 "" 不执行debug绘制)
            this._renderImageComps = [];
            this._builtinUniformMap = new Map();
            const gl = FIREFLYX.App.webgl2;
            this._ignBlcokNames = [FIREFLYX.RenderContext.UBO_CAMERA_BLOCK_NAME, FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME, FIREFLYX.RenderContext.UBO_LIGHT_BLOCK_NAME];
            //
            this._fullClipQuadMesh = FIREFLYX.DefaultAsset.GetMesh(FIREFLYX.DefMesh.QUAD_FULL_CLIP);
            //
            this._fullScreenViewPortRect.set(0, 0, gl.canvas.width, gl.canvas.height);
            //工具材质
            this._screenShadowMapMtr = new FIREFLYX.Material("screenShadowMap");
            this._screenShadowMapMtr.shader = FIREFLYX.DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW);
            this._varianceShadowMapMtr = new FIREFLYX.Material("varianceShadowMap");
            this._varianceShadowMapMtr.shader = FIREFLYX.DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_VSM);
            this._depthMapMtr = new FIREFLYX.Material("depthMap");
            this._depthMapMtr.shader = FIREFLYX.DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_BASE_DEPTH);
            this._fullScreenImageMtr = new FIREFLYX.Material("fullScreen");
            this._fullScreenImageMtr.shader = FIREFLYX.DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_POSTEFF_IMAGE);
            // this._skyBoxMtr = new Material("skyBox");
            // this._skyBoxMtr.shader = DefaultAsset.GetShader(BUILTIN_SHADER_SKY_BOX);
            //内建纹理绑定
            const _buMap = this._builtinUniformMap;
            _buMap.set(FIREFLYX.BuiltinUniform.CAMERA_DEPTH_TEXTURE, this.getCameraDepthTexture.bind(this));
        }
        /** 获取相机的深度纹理 */
        getCameraDepthTexture() {
            let result = null;
            if (this._camDepthTex)
                result = this._camDepthTex.depthStencilTexture;
            return result;
        }
        /**
         * 绑定内建uniform变量函数
         * @param renderCtx
         * @param unbind 是否解绑
         */
        bindBuiltinUniforms(renderCtx, unbind) {
            const _map = renderCtx.builtinUniformMap;
            this._builtinUniformMap.forEach((v, k) => {
                _map.set(k, unbind ? null : v);
            });
        }
        setSetRendererVDataResult(rVDataResult) {
            rVDataResult.material = 0;
            rVDataResult.lenght = 0;
            rVDataResult.bufferOffset = 0;
            rVDataResult.elementType = -1;
            rVDataResult.drawMode = WebGL2RenderingContext.TRIANGLES;
            rVDataResult.frontFace = WebGL2RenderingContext.CCW;
        }
        refreshResolutionVal() {
            const gl = FIREFLYX.App.webgl2;
            this._resolution[0] = gl.canvas.width;
            this._resolution[1] = gl.canvas.height;
        }
        enableFBO(fbo) {
            const gl = FIREFLYX.App.webgl2;
            gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
        }
        disableFBO() {
            const gl = FIREFLYX.App.webgl2;
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        }
        /** 清理gl的状态 */
        closeGLState() {
            const gl = FIREFLYX.App.webgl2;
            gl.bindVertexArray(null);
            gl.bindTexture(gl.TEXTURE_2D, null);
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
        }
        /** 设置 webgl viewPort */
        setGLViewPort(viewRect) {
            const gl = FIREFLYX.App.webgl2;
            //viewport set
            gl.viewport(viewRect.x, viewRect.y, viewRect.w, viewRect.h);
        }
        /** 获取 mian （深度）阴影图 */
        getRTMainShadowMap(index) {
            if (index < 0)
                index = 0;
            if (!this._mainShadowMapList[index]) {
                // this._mainShadowMapList[index] = new RenderTexture2D(`mainShadowTarget${index}`, false, true);
                let rt = FIREFLYX.RenderTexture2D.GetTemporary();
                rt.setFBTexEnable(false, true);
                this._mainShadowMapList[index] = rt;
            }
            return this._mainShadowMapList[index];
        }
        /** 获取 mian VSM （颜色）阴影图 */
        getRTMainVSM(index) {
            if (index < 0)
                index = 0;
            if (!this._mainVSMList[index]) {
                let rt = FIREFLYX.RenderTexture2D.GetTemporary();
                rt.paramSetting.linear = true;
                rt.setFBTexEnable(true);
                this._mainVSMList[index] = rt;
            }
            return this._mainVSMList[index];
        }
        /** 获取 mian 阴影图 */
        getMainShadowMap(index) {
            return this._currRenderCtx.isVSM ? this.getRTMainVSM(index) : this.getRTMainShadowMap(index).depthStencilTexture;
        }
        /** 设置材质CSM 属性 */
        setMateralCSMProperty(_lihgt, _mtr) {
            const l = _lihgt;
            //u_lightVPMatrix 计算
            const lightVPMatrix0 = l != null ? l.getViewProjMatrix(0) : null;
            const lightVPMatrix1 = l != null ? l.getViewProjMatrix(1) : null;
            const lightVPMatrix2 = l != null ? l.getViewProjMatrix(2) : null;
            const lightVPMatrix3 = l != null ? l.getViewProjMatrix(3) : null;
            _mtr.setProperty(`u_lightVPMatrix_${0}`, lightVPMatrix0);
            _mtr.setProperty(`u_mainShadowMap_${0}`, this.getMainShadowMap(0));
            _mtr.setProperty(`u_lightVPMatrix_${1}`, lightVPMatrix1);
            _mtr.setProperty(`u_mainShadowMap_${1}`, this.getMainShadowMap(1));
            _mtr.setProperty(`u_lightVPMatrix_${2}`, lightVPMatrix2);
            _mtr.setProperty(`u_mainShadowMap_${2}`, this.getMainShadowMap(2));
            _mtr.setProperty(`u_lightVPMatrix_${3}`, lightVPMatrix3);
            _mtr.setProperty(`u_mainShadowMap_${3}`, this.getMainShadowMap(3));
            _mtr.setProperty(`u_csm_split`, this._mainShadowCSMSplit);
        }
        /** 创建视锥点 */
        makeFrustumPoints() {
            const result = [];
            for (let i = 0; i < 8; i++) {
                result.push(new FIREFLYX.Vector3());
            }
            return result;
        }
        /**
         * 获取阴影图尺寸
         * @param resolution
         * @returns
         */
        getShadowMapSize(resolution) {
            let size = 0;
            switch (resolution) {
                case FIREFLYX.ShadowMapResolution.LOW:
                    size = 512;
                    break;
                case FIREFLYX.ShadowMapResolution.MEDIUM:
                    size = 1024;
                    break;
                case FIREFLYX.ShadowMapResolution.HIGH:
                    size = 2048;
                    break;
                case FIREFLYX.ShadowMapResolution.VERY_HIGH:
                    size = 4096;
                    break;
                default:
            }
            return size;
        }
        /**
         * 计算 相机视锥 顶点数据
         * @param cam 相机
         * @param renderCtx RenderContext
         */
        calcCamFrustumPoints(cam, renderCtx) {
            const csmCount = renderCtx.shadowCascades;
            const camFList = this._cameraFrustumPointsList;
            const lightFList = this._lightFrustumDataList;
            if (camFList.length < csmCount) {
                const defNum = csmCount - camFList.length;
                for (let i = 0; i < defNum; i++) {
                    camFList.push(this.makeFrustumPoints());
                    lightFList.push(new FIREFLYX.LightFrustumData());
                }
            }
            let lastFar = 0;
            for (let i = 0; i < csmCount; i++) {
                const splitRate = renderCtx.getShadowCascadeSplit(i);
                const n = lastFar;
                const f = renderCtx.shadowDistance * splitRate;
                cam.calcFrustumPoints(n, f, camFList[i]);
                lastFar = f;
            }
        }
        setStencilState(stencilState) {
            if (stencilState == this._currStencilState)
                return;
            const gl = FIREFLYX.App.webgl2;
            const sState = stencilState;
            if (sState == null) {
                gl.disable(gl.STENCIL);
            }
            else {
                gl.enable(gl.STENCIL);
                //front
                gl.stencilFuncSeparate(gl.FRONT, sState.funcFront, sState.refFront, sState.valueMaskFront);
                gl.stencilMaskSeparate(gl.FRONT, sState.writeMaskFront);
                gl.stencilOpSeparate(gl.FRONT, sState.failOpFront, sState.zFailOpFront, sState.passOpFront);
                //back
                gl.stencilFuncSeparate(gl.BACK, sState.funcBack, sState.refBack, sState.valueMaskBack);
                gl.stencilMaskSeparate(gl.BACK, sState.writeMaskBack);
                gl.stencilOpSeparate(gl.BACK, sState.failOpBack, sState.zFailOpBack, sState.passOpBack);
            }
            this._currStencilState = stencilState;
        }
        setBlendState(blendState) {
            if (blendState == this._currBlendState)
                return;
            const gl = FIREFLYX.App.webgl2;
            const bState = blendState;
            if (bState == null) {
                gl.disable(gl.BLEND);
            }
            else {
                gl.enable(gl.BLEND);
                const hasAlphaBlend = bState.dstAlpha != null && bState.srcAlpha != null && bState.equationAlpha != null;
                if (!hasAlphaBlend) {
                    gl.blendEquation(bState.equation);
                    gl.blendFunc(bState.src, bState.dst);
                }
                else {
                    gl.blendEquationSeparate(bState.equation, bState.equationAlpha);
                    gl.blendFuncSeparate(bState.src, bState.dst, bState.srcAlpha, bState.dstAlpha);
                }
            }
            this._currBlendState = blendState;
        }
        setDepthState(depthState) {
            //
            if (depthState == this._currDepthState)
                return;
            const gl = FIREFLYX.App.webgl2;
            //深度设置
            //深度写入
            gl.depthMask(depthState.zWrite);
            //深度测试
            if (depthState.zTestCompareFunc == null) {
                gl.disable(gl.DEPTH_TEST);
            }
            else {
                gl.enable(gl.DEPTH_TEST);
                gl.depthFunc(depthState.zTestCompareFunc);
            }
            this._currDepthState = depthState;
        }
        setRasterState(rasterState) {
            //
            if (rasterState == this._currRasterState)
                return;
            const gl = FIREFLYX.App.webgl2;
            //光栅化状态
            //面剔除
            const cMode = rasterState.cullMode;
            switch (cMode) {
                case FIREFLYX.CullMode.NONE:
                    gl.disable(gl.CULL_FACE);
                    break;
                case FIREFLYX.CullMode.BACK:
                    gl.enable(gl.CULL_FACE);
                    gl.cullFace(gl.BACK);
                    break;
                case FIREFLYX.CullMode.FRONT:
                    gl.enable(gl.CULL_FACE);
                    gl.cullFace(gl.FRONT);
                    break;
                default:
            }
            //多边形偏移
            gl.polygonOffset(rasterState.polygonOffsetFactor, rasterState.polygonOffsetUnits);
            this._currRasterState = rasterState;
        }
        /**
         * 排序 相机Z深度排序渲染队列数据,由于近及远
         * @param rqData 队列数据
         * @param rendererMap 所有render的Map
         * @param viewMatrix 相机view矩阵
         * @param sortReverse 排序颠倒
         */
        sortCamZRenderQueueData(rqData, rendererMap, viewMatrix, sortReverse = false) {
            if (!viewMatrix || !rendererMap)
                return;
            let tempComb = this.help_rdCombArr;
            //填充排序数据
            const rids = rqData.rendererIDs;
            const mtrCounts = rqData.materialCounts;
            const mtrs = rqData.materials;
            let _mtrIdx = 0;
            for (let i = 0, len = rids.length; i < len; i++) {
                const r = rendererMap.get(rids[i]);
                const mtrIdx = _mtrIdx;
                const mtrCount = mtrCounts[i];
                _mtrIdx += mtrCount;
                //RendererCombData
                const rcData = RendererCombData.pool.new();
                rcData.r = r;
                rcData.mtrStartIdx = mtrIdx;
                rcData.mtrCount = mtrCount;
                tempComb.push(rcData);
            }
            //开始排序
            tempComb.sort((a, b) => {
                if (a == b)
                    return 0;
                if (!a || !a.r || !a.r.entity)
                    return sortReverse ? 1 : -1;
                if (!b || !b.r || !b.r.entity)
                    return sortReverse ? -1 : 1;
                //
                const aPos = a.r.entity.transform.localPostion;
                const bPos = b.r.entity.transform.localPostion;
                const v3A = ForwardPipeline.HELP_VEC3;
                const v3B = ForwardPipeline.HELP_VEC3_1;
                FIREFLYX.Vector3.TransformCoordinatesToRef(aPos, viewMatrix, v3A);
                FIREFLYX.Vector3.TransformCoordinatesToRef(bPos, viewMatrix, v3B);
                //view 空间 z 深度排序
                return sortReverse ? v3B.z - v3A.z : v3A.z - v3B.z;
            });
            //
            const tempMtrs = this.help_MtrArr;
            tempMtrs.length = 0;
            //梳理到 RenderQueueData
            for (let i = 0, len = tempComb.length; i < len; i++) {
                //
                const val = tempComb[i];
                const r = val.r;
                if (!r)
                    continue;
                const mtrIdx = val.mtrStartIdx;
                const mtrCount = val.mtrCount;
                //renderIDs
                rids[i] = r.getInstanceID();
                //mtrCounts
                mtrCounts[i] = mtrCount;
                //mtrIndex
                for (let j = 0, len1 = mtrCount; j < len1; j++) {
                    const mtr = mtrs[mtrIdx + j];
                    tempMtrs.push(mtr);
                }
            }
            //copyFrom
            // mtrs.copyFrom(tempMtrs);
            let len = tempMtrs.length;
            mtrs.length = 0;
            for (let i = 0; i < len; i++) {
                mtrs[i] = tempMtrs[i];
            }
            //清理
            const rcDataPool = RendererCombData.pool;
            for (let i = 0, len = tempComb.length; i < len; i++) {
                rcDataPool.delete(tempComb[i]);
            }
            tempComb.length = 0;
        }
        /**
         * 材质组信息整理分析
         */
        materialGroupAnalyse(rendererMap, cpRendererIDs, mgInfoSetMap, shadowCasterRList) {
            //遍历所有render
            // const rendererMap = renderCtx.rendererMap;
            const cprIDsLen = cpRendererIDs.length;
            let cprIdx = 0;
            while (cprIdx < cprIDsLen) {
                const id = cpRendererIDs[cprIdx];
                cprIdx++;
                if (id == null)
                    continue;
                const r = rendererMap.get(id);
                if (!r)
                    continue;
                let mgs = r.materialGroupList;
                let mgsLen = mgs.length;
                if (mgsLen == 0)
                    return; //没有材质数据，跳过。
                let mgInfoSet = RendererMaterialInfo.pool.new();
                mgInfoSet.rendererID = id;
                //      每组 分配一个infoSet对象，放入一个字典
                mgInfoSetMap.set(id, mgInfoSet);
                let isShadowCasterR = false;
                //      遍历材质，分析整理到info对象
                for (let i = 0; i < mgsLen; i++) {
                    let info = mgInfoSet.addMGInfo();
                    let isShadowCasterM = false;
                    let isTransparentM = false;
                    //
                    let mg = mgs[i];
                    let msLen = mg.length;
                    //主材质
                    info.mainMaterial = mg[0];
                    //遍历 材质组
                    for (let j = 0; j < msLen; j++) {
                        let m = mg[j];
                        //透明材质
                        if (m.blendState != null) {
                            isTransparentM = true;
                            if (!info.transparents) {
                                info.transparents = [];
                            }
                            info.transparents.push(m);
                        }
                        //阴影投射
                        if (r.castShadows && m.lightMode == FIREFLYX.RenderPipeline.LM_SHADOW_CASTER) {
                            isShadowCasterM = true;
                            if (!info.shadowCasters) {
                                info.shadowCasters = [];
                            }
                            info.shadowCasters.push(m);
                        }
                        //整理安排队列
                        if (m.lightMode == FIREFLYX.RenderPipeline.LM_FORWARD || m.lightMode == FIREFLYX.RenderPipeline.LM_ALWAYS) {
                            const fRQ = this._forwardRenderQueueC.getQueue(m.renderQueue);
                            fRQ.materials.push(m);
                            if (fRQ.rendererIDs[fRQ.rendererIDs.length - 1] != id) {
                                fRQ.rendererIDs.push(id);
                                fRQ.materialCounts.push(1);
                            }
                            else {
                                const lastIndex = fRQ.materialCounts.length - 1;
                                const lastVal = fRQ.materialCounts[lastIndex];
                                fRQ.materialCounts[lastIndex] = lastVal + 1; //数增1
                            }
                        }
                    }
                    //阴影pass 收集 材质
                    if (isShadowCasterM) {
                        isShadowCasterR = true;
                        mgInfoSet.shadowCasterMGIndexs.push(i);
                    }
                    //      填充到队列（ 非透明、透明混合、透明测试）
                    if (isTransparentM) {
                        mgInfoSet.transparentMGIndexs.push(i);
                    }
                }
                //阴影pass 收集 renderer
                if (isShadowCasterR) {
                    shadowCasterRList.push(mgInfoSet);
                }
            }
        }
        /**
         * 相机深度图更新
         */
        renderCamDepthTex(renderCtx) {
            //检查一下是否要 更新 相机深度图
            //light 检查
            const mainLight = renderCtx.lightCollector.mainLight;
            let needMainLightShadow = mainLight != null && mainLight.entity != null;
            let needUpdateCamDepthTex2D = needMainLightShadow;
            let needUpdateCamDepthTexCube = false;
            const scrlist = this._shadowCasterRList;
            if (needUpdateCamDepthTex2D) {
                if (!this._camDepthTex) {
                    this._camDepthTex = FIREFLYX.RenderTexture2D.GetTemporary();
                    this._camDepthTex.setFBTexEnable(false, true, false);
                }
                //相机的深度图2D渲染
                this.renderCameraDepthTex2D(renderCtx, this._camDepthTex, scrlist);
            }
            if (needUpdateCamDepthTexCube) {
                this.renderCameraDepthTexCube();
            }
        }
        /**
         * 渲染整个队列
         * @param materials 材质列表
         * @param rendererIDs 渲染器列表
         * @param mtrCounts 材质数量列表
         * @param isTransparent 是否半透明混合
         */
        renderByQueue(renderCtx, materials, rendererIDs, mtrCounts, isTransparent) {
            const rendererMap = renderCtx.rendererMap;
            if (!rendererMap)
                return;
            const gl = renderCtx.webgl2;
            const materialDirMap = this.help_StrBoolMap;
            materialDirMap.clear();
            const rVDataResult = this.help_RVDataResult;
            this.setSetRendererVDataResult(rVDataResult);
            this.refreshResolutionVal();
            //last cache
            let lastMaterial = null;
            let lastShaderCompiled = null;
            let lastFrontFace = null;
            let currMbuoy = 0;
            //遍历 元素
            for (let i = 0, len1 = rendererIDs.length; i < len1; i++) {
                const mbuoy = currMbuoy;
                const mCount = mtrCounts[i];
                if (mCount == null)
                    continue;
                currMbuoy += mCount;
                const id = rendererIDs[i];
                if (id == null)
                    continue;
                const r = rendererMap.get(id);
                if (!r)
                    continue;
                const model = r.entity;
                if (!model)
                    continue;
                //绑定ubo
                //灯光 设置提交 (每个模型最大接收光源有上限)
                //模型 设置提交
                const modelUBO = model.transform.ubo;
                model.transform.refreshUboData();
                gl.bindBufferRange(gl.UNIFORM_BUFFER, FIREFLYX.UBOBindingEnum.MODEL, modelUBO, 0, model.transform.uboBlockSize);
                //筛选渲染的材质
                //逐 mesh , 的subMesh 渲染
                const vDataLen = r.getVertexDataCount();
                let idx = 0;
                while (idx < vDataLen) {
                    const _idx = idx;
                    //增加计数
                    idx++;
                    //渲染单个 模型
                    //顶点数据 设置提交
                    r.uploadVertexData(_idx, rVDataResult);
                    const targetMIdx = rVDataResult.material;
                    //获取相应的材质组
                    // const mIdx = mbuoy + (_idx < mCount ? _idx : 0);
                    const mIdx = mbuoy + (!isNaN(targetMIdx) && targetMIdx < mCount && targetMIdx > 0 ? targetMIdx : 0);
                    const m = materials[mIdx];
                    if (!m)
                        continue;
                    const sh = m.shader;
                    const cSh = sh.getCompiled(m.getKeywordID());
                    if (!cSh)
                        continue;
                    const sp = cSh.program;
                    if (!sp)
                        continue;
                    //填充材质数据
                    if (!materialDirMap.get(m.UUID)) {
                        materialDirMap.set(m.UUID, true);
                        //阴影数据
                        //是否接收阴影
                        let receiveShadows = r.receiveShadows && !isTransparent && this._shadowCasterRList.length > 0;
                        //屏幕空间 阴影图 属性设置
                        let screenShadowMap = null;
                        let resolution = null;
                        if (receiveShadows) { //***之后要用 shader 关键字 切换变体的方式处理
                            screenShadowMap = this._screenShadowTex;
                            resolution = this._resolution;
                        }
                        m.setProperty("u_screenShadowTex", screenShadowMap);
                        m.setProperty("u_resolution", resolution);
                    }
                    //这里需要好好考虑，比如 shader 变体 、 skin 和 非skin 怎么处理
                    const sameWithLastMaterial = lastMaterial == m;
                    //相同的 shader 分支（变体）
                    if (!sameWithLastMaterial || lastShaderCompiled != cSh) {
                        gl.useProgram(sp);
                        //ubo index 绑定到 shaderProgram-------------
                        // const invalidBlockIdx = 4294967295; //getUniformBlockIndex 无效返回值
                        if (cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_CAMERA_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_CAMERA_BLOCK_NAME], FIREFLYX.UBOBindingEnum.CAMERA);
                        }
                        if (cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME], FIREFLYX.UBOBindingEnum.MODEL);
                        }
                        if (cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_LIGHT_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_LIGHT_BLOCK_NAME], FIREFLYX.UBOBindingEnum.LIGHT);
                        }
                        //纹理Activator
                        renderCtx.textrueActivator.reset();
                        //材质 uniform 上传---------------------------
                        m.uploadUniform(renderCtx);
                    }
                    //和上一个渲染的材质是否为同一个,跳过状态修改
                    if (!sameWithLastMaterial) {
                        //材质状态 设置提交----------------------------
                        this.setStencilState(m.stencilState);
                        this.setBlendState(m.blendState);
                        this.setDepthState(m.depthState);
                        this.setRasterState(m.rasterState);
                    }
                    //正面顺序
                    if (rVDataResult.frontFace != lastFrontFace) {
                        gl.frontFace(rVDataResult.frontFace);
                    }
                    //绘制
                    const drawInsCount = 1; //GPUInstance 预留
                    const isElementMode = rVDataResult.elementType != -1;
                    if (isElementMode) {
                        gl.drawElementsInstanced(rVDataResult.drawMode, rVDataResult.lenght, rVDataResult.elementType, rVDataResult.bufferOffset, drawInsCount);
                    }
                    else {
                        gl.drawArraysInstanced(rVDataResult.drawMode, rVDataResult.bufferOffset, rVDataResult.lenght, drawInsCount);
                    }
                    //cache last
                    lastMaterial = m;
                    lastShaderCompiled = cSh;
                    lastFrontFace = rVDataResult.frontFace;
                }
            }
            //清理
            materialDirMap.clear();
        }
        /** 渲染forwardPass */
        renderForwardPass(renderCtx, camera) {
            const rendererMap = renderCtx.rendererMap;
            if (!rendererMap)
                return;
            this.setGLViewPort(renderCtx.curCamPixelViewPort);
            //前向渲染通道 渲染器队列收集器
            const forwardRQC = this._forwardRenderQueueC;
            //遍历 队列
            const rqIds = forwardRQC.renderQueueIDs;
            const maxTransparentRQ = FIREFLYX.RenderContext.RENDER_QUEUE_TRANSPARENT + 2000;
            const maxAlphaTestRQ = FIREFLYX.RenderContext.RENDER_QUEUE_ALPHA_TEST + 2000;
            for (let i = 0, len = rqIds.length; i < len; i++) {
                const rqid = rqIds[i];
                if (rqid == null)
                    continue;
                const data = forwardRQC.dataMap.get(rqid);
                if (data == null)
                    continue;
                //  按Z排序
                //  透明强制排序
                //  非透明可选 （排序CPU 负荷，不排序 GPU负荷）
                let isTransparent = false;
                if (rqid >= FIREFLYX.RenderContext.RENDER_QUEUE_ALPHA_TEST && rqid < maxAlphaTestRQ) {
                    //alpha test 物
                }
                else if (rqid >= FIREFLYX.RenderContext.RENDER_QUEUE_TRANSPARENT && rqid < maxTransparentRQ) {
                    isTransparent = true;
                    //是透明物 ， 需要相机z 深度排序，需要由远及近 ,所以 sortReverse
                    this.sortCamZRenderQueueData(data, renderCtx.rendererMap, camera.getViewMatrix(), true);
                }
                this.renderByQueue(renderCtx, data.materials, data.rendererIDs, data.materialCounts, isTransparent);
            }
            //clear
            forwardRQC.clear();
            //绘制完毕清理状态
            this.closeGLState();
        }
        /** 渲染阴影投射通道 */
        renderShadowCasterPass(renderCtx) {
            const scrlist = this._shadowCasterRList;
            if (scrlist.length < 1)
                return; //不需要 shadow
            const mainLight = renderCtx.lightCollector.mainLight;
            let needMainLightShadow = mainLight != null && mainLight.entity != null;
            const needAddLightShadow = false;
            if (!needMainLightShadow && !needAddLightShadow)
                return; //不需要 shadow
            //主光源
            if (needMainLightShadow) {
                //阴影的灯光级联深度图渲染
                this.renderDirLightCSM(renderCtx, scrlist, this._mainShadowCSMSplit, this._lightFrustumDataList, this._cameraFrustumPointsList, mainLight);
            }
            //addLight 阴影
            //其他光源
            //
            if (!this._screenShadowTex) {
                this._screenShadowTex = FIREFLYX.RenderTexture2D.GetTemporary();
                this._screenShadowTex.setFBTexEnable(true);
            }
            //汇集阴影 到 屏幕空间
            this.renderCollectShadow(renderCtx, this._screenShadowTex, this._camDepthTex, this._screenShadowMapMtr);
            //绘制完毕清理状态
            this.closeGLState();
            //关闭FBO
            this.disableFBO();
        }
        /** 屏幕空间阴影Map 到 屏幕空间方差阴影Map*/
        renderVSM(lightDepthMap, vsm, ishorizontalPass = false) {
            //材质
            let mat = this._varianceShadowMapMtr;
            this._fboResolution[0] = vsm.width;
            this._fboResolution[1] = vsm.width;
            mat.setProperty("u_resolution", this._fboResolution);
            mat.setProperty("u_samples", this._currRenderCtx.vsmSampleCount);
            mat.setProperty("u_radius", this._currRenderCtx.vsmSampleRadius);
            mat.setProperty("u_horizontalPass", ishorizontalPass ? 1 : 0);
            FIREFLYX.Graphics.Blit(lightDepthMap, vsm, mat);
        }
        /** 渲染 汇集阴影 （输出 屏幕空间阴影Map）*/
        renderCollectShadow(renderCtx, screenSM, camDepthTex, _mtr) {
            //所有阴影绘制到屏幕空间
            const gl = FIREFLYX.App.webgl2;
            const viewPort = renderCtx.curCamPixelViewPort;
            screenSM.setSize(viewPort.w, viewPort.h); //设置纹理尺寸
            this._fboResolution[0] = viewPort.w;
            this._fboResolution[1] = viewPort.h;
            this.enableFBO(screenSM.fbo);
            //viewPort
            this.setGLViewPort(viewPort);
            //清理 
            gl.clearColor(1, 1, 1, 1); //backclear color
            gl.clearDepth(1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            //主光源 阴影处理
            const mainLight = renderCtx.lightCollector.mainLight;
            if (mainLight != null) {
                const tex = camDepthTex.depthStencilTexture;
                //材质参数设置
                _mtr.setProperty("u_mainTex", tex);
                //u_resolution
                this.refreshResolutionVal();
                _mtr.setProperty("u_resolution", this._resolution);
                //shadow CSM 属性
                this.setMateralCSMProperty(renderCtx.lightCollector.mainLight, _mtr);
                //渲染
                const quadMesh = this._fullClipQuadMesh;
                const sh = _mtr.shader;
                const cSH = sh.getCompiled(_mtr.getKeywordID());
                const sp = cSH.program;
                //WebGLProgram use
                gl.useProgram(sp);
                //vao
                gl.bindVertexArray(quadMesh.VAO); //启用vao
                //纹理Activator
                renderCtx.textrueActivator.reset();
                //uniform 数据提交
                _mtr.uploadUniform(renderCtx);
                //绘制贴屏片 后渲染效果
                gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);
                // //绘制完毕清理状态
                // this.closeGLState();
                //清理材质
                _mtr.setProperty("u_mainTex", null);
                _mtr.setProperty("u_resolution", this._resolution);
            }
            //close
            this.disableFBO();
            this.closeGLState();
        }
        /**
         * 渲染 相机2D 到深度纹理
         * @param renderCtx
         * @param rendererList 渲染器列表
         * @param vpMatrix vp矩阵
         * @param skipTransparent 是否跳过半透明混合材质
         */
        renderDepthTex2DByList(renderCtx, rendererList, vpMatrix, skipTransparent = false) {
            const gl = FIREFLYX.App.webgl2;
            const rVDataResult = {
                material: 0,
                lenght: 0,
                bufferOffset: 0,
                elementType: -1,
                drawMode: gl.TRIANGLES,
                frontFace: gl.CCW
            };
            const rendererMap = renderCtx.rendererMap;
            //      渲染状态统一设置 , shadowCaster 统一模式绘制
            this.setStencilState(null);
            this.setBlendState(null);
            this.setDepthState(FIREFLYX.DepthState.getState(true));
            //
            let lastFrontFace = null;
            let isFirstUseDepthMtr = true;
            //材质
            const m = this._depthMapMtr;
            const sh = m.shader;
            const cSh = sh.getCompiled(m.getKeywordID());
            const sp = cSh.program;
            m.setProperty(`u_viewProjMtx`, vpMatrix);
            //      遍历renderer渲染模型
            for (let i = 0, len = rendererList.length; i < len; i++) {
                //
                const rmi = rendererList[i];
                if (skipTransparent && rmi.transparentMGIndexs.length > 0)
                    continue; //跳过透明物的渲染
                const r = rendererMap.get(rmi.rendererID);
                const rpCount = r.getVertexDataCount();
                const model = r.entity;
                //to Refresh data
                model.transform.getMatrix();
                //遍历 render 所有的 VertexData ，并用 深度材质渲染一遍
                for (let j = 0, len1 = rpCount; j < len1; j++) {
                    //
                    //绑定ubo
                    //灯光 设置提交 (每个模型最大接收光源有上限)
                    //模型 设置提交
                    const modelUBO = model.transform.ubo;
                    model.transform.refreshUboData();
                    gl.bindBufferRange(gl.UNIFORM_BUFFER, FIREFLYX.UBOBindingEnum.MODEL, modelUBO, 0, model.transform.uboBlockSize);
                    //渲染
                    r.uploadVertexData(j, rVDataResult);
                    if (isFirstUseDepthMtr) {
                        isFirstUseDepthMtr = false;
                        gl.useProgram(sp);
                        if (cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME], FIREFLYX.UBOBindingEnum.MODEL);
                        }
                        this.setRasterState(m.rasterState);
                        //纹理Activator
                        renderCtx.textrueActivator.reset();
                        m.uploadUniform(renderCtx);
                    }
                    //正面顺序
                    if (rVDataResult.frontFace != lastFrontFace) {
                        gl.frontFace(rVDataResult.frontFace);
                    }
                    //绘制
                    const drawInsCount = 1;
                    const isElementMode = rVDataResult.elementType != -1;
                    if (isElementMode) {
                        gl.drawElementsInstanced(rVDataResult.drawMode, rVDataResult.lenght, rVDataResult.elementType, rVDataResult.bufferOffset, drawInsCount);
                    }
                    else {
                        gl.drawArraysInstanced(rVDataResult.drawMode, rVDataResult.bufferOffset, rVDataResult.lenght, drawInsCount);
                    }
                    //last cache
                    lastFrontFace = rVDataResult.frontFace;
                }
            }
        }
        /**
         * 渲染 到阴影纹理
         * @param renderCtx
         * @param rendererList 渲染器列表
         * @param vpMatrix vp矩阵
         */
        renderShadowMapByList(renderCtx, rendererList, vpMatrix, skipTransparent = false) {
            const gl = FIREFLYX.App.webgl2;
            const rVDataResult = this.help_RVDataResult;
            this.setSetRendererVDataResult(rVDataResult);
            const rendererMap = renderCtx.rendererMap;
            //      渲染状态统一设置 , shadowCaster 统一模式绘制
            this.setStencilState(null);
            this.setBlendState(null);
            this.setDepthState(FIREFLYX.DepthState.getState(true));
            //
            const materialDirMap = this.help_StrBoolMap;
            materialDirMap.clear();
            let lastFrontFace = null;
            let lastMaterial = null;
            //u_lightVPMatrix 计算
            // const lightVPMatrix = light.getViewProjMatrix(csmIndex);
            const lightVPMatrix = vpMatrix;
            //      遍历renderer渲染模型
            for (let i = 0, len = rendererList.length; i < len; i++) {
                //
                const rmi = rendererList[i];
                const scMGIdxs = rmi.shadowCasterMGIndexs;
                const r = rendererMap.get(rmi.rendererID);
                if (skipTransparent && rmi.transparentMGIndexs.length > 0)
                    continue; //跳过透明物的渲染
                const rpCount = r.getVertexDataCount();
                for (let j = 0, len1 = scMGIdxs.length; j < len1; j++) {
                    const scMGIdx = scMGIdxs[j];
                    let pIdx = scMGIdx >= rpCount ? rpCount - 1 : scMGIdx; //处理 子网格数 少于 材质数的情况
                    const mgInfo = rmi.getMGInfo(scMGIdx);
                    const model = r.entity;
                    const m = mgInfo.shadowCasters[0]; //暂时处理，材质组中 多个shadowCasters 情况要处理？
                    const mainM = mgInfo.mainMaterial;
                    const sh = m.shader;
                    const cSh = sh.getCompiled(m.getKeywordID());
                    if (!cSh)
                        continue;
                    const sp = cSh.program;
                    if (!sp)
                        continue;
                    //填充阴影数据
                    if (!materialDirMap.get(m.UUID)) {
                        materialDirMap.set(m.UUID, true);
                        //vp矩阵 属性
                        const lVPMatName = `u_viewProjMtx`;
                        const mainTexName = `u_mainTex`;
                        const mainColorName = `u_mainColor`;
                        const resolutionName = `u_resolution`;
                        m.setProperty(lVPMatName, lightVPMatrix);
                        //alpha 属性
                        let mainTex = null;
                        let mainColor = null;
                        if (mainM) {
                            mainTex = mainM.getProperty(mainTexName);
                            mainColor = mainM.getProperty(mainColorName);
                        }
                        m.setProperty(mainTexName, mainTex);
                        m.setProperty(mainColorName, mainColor);
                        m.setProperty(resolutionName, this._fboResolution);
                    }
                    //to Refresh data
                    model.transform.getMatrix();
                    //绑定ubo
                    //灯光 设置提交 (每个模型最大接收光源有上限)
                    //模型 设置提交
                    const modelUBO = model.transform.ubo;
                    model.transform.refreshUboData();
                    gl.bindBufferRange(gl.UNIFORM_BUFFER, FIREFLYX.UBOBindingEnum.MODEL, modelUBO, 0, model.transform.uboBlockSize);
                    //渲染
                    r.uploadVertexData(pIdx, rVDataResult);
                    if (m != lastMaterial) {
                        gl.useProgram(sp);
                        if (cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME] != null) {
                            gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[FIREFLYX.RenderContext.UBO_MODEL_BLOCK_NAME], FIREFLYX.UBOBindingEnum.MODEL);
                        }
                        // gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_CAMERA_BLOCK_NAME], UBOBindingEnum.CAMERA);
                        // gl.uniformBlockBinding(sp, cSh.unifBlockNameIndexMap[RenderContext.UBO_LIGHT_BLOCK_NAME], UBOBindingEnum.LIGHT);
                        this.setRasterState(m.rasterState);
                        //纹理Activator
                        renderCtx.textrueActivator.reset();
                        m.uploadUniform(renderCtx);
                    }
                    //正面顺序
                    if (rVDataResult.frontFace != lastFrontFace) {
                        gl.frontFace(rVDataResult.frontFace);
                    }
                    //绘制
                    const drawInsCount = 1;
                    const isElementMode = rVDataResult.elementType != -1;
                    if (isElementMode) {
                        gl.drawElementsInstanced(rVDataResult.drawMode, rVDataResult.lenght, rVDataResult.elementType, rVDataResult.bufferOffset, drawInsCount);
                    }
                    else {
                        gl.drawArraysInstanced(rVDataResult.drawMode, rVDataResult.bufferOffset, rVDataResult.lenght, drawInsCount);
                    }
                    //last cache
                    lastFrontFace = rVDataResult.frontFace;
                    lastMaterial = m;
                }
            }
            //清理
            materialDirMap.clear();
        }
        /**
         * 渲染 主方向光 的级联阴影图
         * @param renderCtx
         * @param rendererList 渲染器列表
         * @param shadowCSMSplit 级联阴影段落区间划分参数
         * @param lightFrustumDataList 光的包围锥体数据列表
         * @param cameraFrustumPointsList 相机的包围锥体顶点数据列表
         * @param light 主方向光对象
         */
        renderDirLightCSM(renderCtx, rendererList, shadowCSMSplit, lightFrustumDataList, cameraFrustumPointsList, light) {
            const gl = FIREFLYX.App.webgl2;
            //阴影的灯光级联深度图渲染
            //填充级联划分值
            shadowCSMSplit.forEach((v, i, arr) => { arr[i] = 1; });
            for (let i = 0, len = renderCtx.shadowCascades - 1; i < len; i++) {
                shadowCSMSplit[i] = renderCtx.getShadowCascadeSplit(i);
            }
            //阴影Z方向的最大距离
            shadowCSMSplit[3] = renderCtx.shadowDistance;
            //计算 灯光视锥数据 (级联阴影)
            const csmCount = renderCtx.shadowCascades;
            for (let i = 0; i < csmCount; i++) {
                const lFData = lightFrustumDataList[i];
                const camF = cameraFrustumPointsList[i];
                FIREFLYX.Light.calcDirLightFrustumData(camF, light.entity.transform.rotation, lFData);
                light.setVPMatrix(i, lFData.w, lFData.h, lFData.far, lFData.position);
            }
            //阴影图 尺寸
            const shaodwMapSize = this.getShadowMapSize(light.shadowMapResolution);
            const mTexW = shaodwMapSize;
            const mTexH = shaodwMapSize;
            //view port 设置
            gl.viewport(0, 0, mTexW, mTexH);
            let vsmHTempRT = null;
            if (renderCtx.isVSM) {
                vsmHTempRT = FIREFLYX.RenderTexture2D.GetTemporary();
                vsmHTempRT.paramSetting.linear = true;
                vsmHTempRT.setFBTexEnable(true);
                vsmHTempRT.setSize(mTexW, mTexH);
            }
            //遍历所有阴影级联
            for (let csmIndex = 0; csmIndex < csmCount; csmIndex++) {
                // this.enableFBO(ForwardPipeline.MAIN_SHADOWCASTER_FBO, mTexW, mTexH);
                const mainShadowMap = this.getRTMainShadowMap(csmIndex);
                mainShadowMap.setSize(mTexW, mTexH);
                this._fboResolution[0] = mTexW;
                this._fboResolution[1] = mTexH;
                //  深度渲染到2d纹理
                //      激活深度FBO
                this.enableFBO(mainShadowMap.fbo);
                //设置深度 ZWrite状态
                this.setDepthState(FIREFLYX.DepthState.getState(true));
                gl.clearDepth(1.0);
                //clear depth , 深度 ZWrite 不为 true，清理是无效的
                gl.clear(gl.DEPTH_BUFFER_BIT);
                //      light 视锥剔除
                const lightVPMatrix = light.getViewProjMatrix(csmIndex);
                //渲染 depth 队列
                this.renderShadowMapByList(renderCtx, rendererList, lightVPMatrix);
                //VSM 模式
                if (renderCtx.isVSM) {
                    let vsm = this.getRTMainVSM(csmIndex);
                    vsm.setSize(mTexW, mTexH);
                    this.renderVSM(mainShadowMap.depthStencilTexture, vsmHTempRT, false);
                    this.renderVSM(vsmHTempRT, vsm, true);
                }
            }
            if (renderCtx.isVSM) {
                FIREFLYX.RenderTexture2D.ReleaseTemporary(vsmHTempRT);
            }
            //清理
            this.closeGLState();
        }
        /** 渲染 相机的深度2D纹理 */
        renderCameraDepthTex2D(renderCtx, depthTex, rendererList) {
            const gl = FIREFLYX.App.webgl2;
            const viewPort = renderCtx.curCamPixelViewPort;
            depthTex.setSize(viewPort.w, viewPort.h);
            this._fboResolution[0] = viewPort.w;
            this._fboResolution[1] = viewPort.h;
            //view port 设置
            gl.viewport(0, 0, depthTex.width, depthTex.height);
            //fbo 状态开启
            this.enableFBO(depthTex.fbo);
            this.setDepthState(FIREFLYX.DepthState.getState(true)); //zwrite true 才能清理 depth
            //清理 
            gl.clearDepth(1.0);
            gl.clear(gl.DEPTH_BUFFER_BIT);
            //遍历绘制
            const vPMatrix = renderCtx.camViewProjMatrix;
            //渲染 depth 队列
            this.renderDepthTex2DByList(renderCtx, rendererList, vPMatrix, true);
            //fbo 状态关闭
            this.disableFBO();
            //clear
            this.closeGLState();
        }
        /** 渲染 相机的深度cube纹理 */
        renderCameraDepthTexCube() {
            //
        }
        /** 渲染 FBO 到 后处理全屏效果*/
        renderPostEffQuad(renderCtx, material, viewPortRect) {
            const gl = renderCtx.webgl2;
            const quadMesh = this._fullClipQuadMesh;
            const sh = material.shader;
            const cSH = sh.getCompiled(material.getKeywordID());
            const sp = cSH.program;
            //viewport set
            this.setGLViewPort(viewPortRect);
            //pass clear
            gl.clearColor(0, 0, 0, 0); //backclear color
            gl.clearDepth(1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            //WebGLProgram use
            gl.useProgram(sp);
            //vao
            gl.bindVertexArray(quadMesh.VAO); //启用vao
            //纹理Activator
            renderCtx.textrueActivator.reset();
            //uniform 数据提交
            material.uploadUniform(renderCtx);
            //绘制贴屏片 后渲染效果
            gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);
            //绘制完毕清理状态
            this.closeGLState();
        }
        /**
         * 渲染 调试帧
         * @param renderCtx
         * @param debugType 调试类型
         */
        renderDebugFrame(renderCtx, debugType) {
            const gl = renderCtx.webgl2;
            let shKey = "";
            let tex = null;
            let _mtr = this._shadowDebugTexMtr;
            switch (debugType) {
                case "shadowMap":
                    shKey = FIREFLYX.BUILTIN_SHADER_POSTEFF_DEPTH_TEX;
                    if (!_mtr) {
                        this._shadowDebugTexMtr = _mtr = new FIREFLYX.Material("shadowDepthMtr");
                        _mtr.shader = FIREFLYX.DefaultAsset.GetShader(shKey);
                    }
                    tex = this.getRTMainShadowMap(0).depthStencilTexture;
                    _mtr.setProperty("u_mainTex", tex);
                    break;
                case "camDepthMap":
                    shKey = FIREFLYX.BUILTIN_SHADER_POSTEFF_DEPTH_TEX;
                    if (!_mtr) {
                        this._shadowDebugTexMtr = _mtr = new FIREFLYX.Material("shadowDepthMtr");
                        _mtr.shader = FIREFLYX.DefaultAsset.GetShader(shKey);
                    }
                    if (this._camDepthTex)
                        tex = this._camDepthTex.depthStencilTexture;
                    _mtr.setProperty("u_mainTex", tex);
                    _mtr.setProperty("u_depthScale", 500);
                    break;
                case "screenShadowMap":
                    shKey = FIREFLYX.BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW;
                    if (!_mtr) {
                        this._shadowDebugTexMtr = _mtr = new FIREFLYX.Material("shadowDepthMtr");
                    }
                    if (this._camDepthTex)
                        tex = this._camDepthTex.depthStencilTexture;
                    _mtr.shader = FIREFLYX.DefaultAsset.GetShader(shKey);
                    _mtr.setProperty("u_mainTex", tex);
                    //u_resolution
                    let _resolution = new FIREFLYX.Vector2(gl.canvas.width, gl.canvas.height);
                    _mtr.setProperty("u_resolution", _resolution);
                    //shadow CSM 属性
                    this.setMateralCSMProperty(renderCtx.lightCollector.mainLight, _mtr);
                    break;
                default:
            }
            //
            this._fullScreenViewPortRect.set(0, 0, gl.canvas.width, gl.canvas.height);
            this.renderPostEffQuad(renderCtx, this._shadowDebugTexMtr, this._fullScreenViewPortRect);
            // Graphics.DrawFullQuad(this._shadowDebugTexMtr as Material, this._fullScreenViewPortRect);
            //
            // this.closeGLState();
        }
        /** 设置相机 */
        useCamera(cam, renderCtx) {
            const gl = FIREFLYX.App.webgl2;
            //
            //相机 viewPort 设置
            const pViewPort = renderCtx.curCamPixelViewPort;
            const canvasW = gl.canvas.width;
            const canvasH = gl.canvas.height;
            const vpRect = cam.viewPortRect;
            pViewPort.set(canvasW * vpRect.x, canvasH * vpRect.y, canvasW * vpRect.w, canvasH * vpRect.h);
            //viewProjMtx 计算
            const camPMtx = cam.getProjectionMatrx();
            const camVMtx = cam.getViewMatrix();
            camVMtx.multiplyToRef(camPMtx, renderCtx.camViewProjMatrix);
            //相机视锥顶点计算
            this.calcCamFrustumPoints(cam, renderCtx);
            //相机UBO 设置
            const camUBO = cam.ubo;
            cam.refreshUboData();
            gl.bindBufferRange(gl.UNIFORM_BUFFER, FIREFLYX.UBOBindingEnum.CAMERA, camUBO, 0, cam.uboBlockSize);
        }
        /**
         * 渲染 相机 帧清理
         */
        renderCameraFrameClear(cam, renderCtx) {
            const gl = renderCtx.webgl2;
            const bg = cam.backGround;
            this.setDepthState(FIREFLYX.DepthState.getState(true)); //zwrite true  才能清理 depth
            gl.clearDepth(1.0);
            switch (cam.clearMode) {
                case FIREFLYX.CameraClearMode.SKY_BOX:
                case FIREFLYX.CameraClearMode.COLOR:
                    gl.clearColor(bg.r, bg.g, bg.b, bg.a); //backclear color
                    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                    break;
                case FIREFLYX.CameraClearMode.DEPTH_ONLY:
                    gl.clear(gl.DEPTH_BUFFER_BIT);
                    break;
                default:
            }
        }
        /** 临时renderTexture2D 对象清理 */
        rtTemporaryRelease() {
            //相机深度rt
            if (this._camDepthTex) {
                FIREFLYX.RenderTexture2D.ReleaseTemporary(this._camDepthTex);
                this._camDepthTex = null;
            }
            //屏幕空间阴影 rt
            if (this._screenShadowTex) {
                FIREFLYX.RenderTexture2D.ReleaseTemporary(this._screenShadowTex);
                this._screenShadowTex = null;
            }
            //mainShadowMap rts
            this.rtListRelease(this._mainShadowMapList);
            this.rtListRelease(this._mainVSMList);
        }
        //清理 rt列表
        rtListRelease(list) {
            if (!list || list.length < 1)
                return;
            for (let i = 0, len = list.length; i < len; i++) {
                const st = list[i];
                if (!st)
                    continue;
                FIREFLYX.RenderTexture2D.ReleaseTemporary(st);
            }
            list.length = 0;
        }
        /** 渲染到纹理 前期操作 */
        renderImagePro(renderCtx, srcRT, destRT) {
            let gl = renderCtx.webgl2;
            const viewPort = renderCtx.curCamPixelViewPort;
            srcRT.setSize(viewPort.w, viewPort.h);
            destRT.setSize(viewPort.w, viewPort.h);
            //清理 dest buffer
            this.enableFBO(destRT.fbo);
            gl.clearColor(0, 0, 0, 0); //backclear color
            gl.clear(gl.COLOR_BUFFER_BIT);
            //切换到 srcRT 接收 pass的绘制
            this.enableFBO(srcRT.fbo);
        }
        /** 渲染到纹理 后期操作 */
        renderImagePost(renderCtx, renderImages, srcRT, destRT) {
            //执行 后渲染到纹理
            for (let j = 0, len1 = renderImages.length; j < len1; j++) {
                let c = renderImages[j];
                c.onRenderImage(srcRT, destRT);
            }
            //最终渲染到屏幕
            this._fullScreenImageMtr.setProperty("u_mainTex", destRT);
            this.renderPostEffQuad(renderCtx, this._fullScreenImageMtr, renderCtx.curCamPixelViewPort);
        }
        /** 渲染天空盒 到纹理 */
        renderSkyBox(renderCtx) {
            const material = renderCtx.skyboxMaterial;
            if (!material)
                return;
            //准备好 天空盒的 cubemap 、贴满clip空间的mesh、天空盒的着色器
            const gl = renderCtx.webgl2;
            const sh = material.shader;
            const cSH = sh.getCompiled(material.getKeywordID());
            const sp = cSH.program;
            const quadMesh = this._fullClipQuadMesh;
            //执行绘制
            //WebGLProgram use
            gl.useProgram(sp);
            //vao
            gl.bindVertexArray(quadMesh.VAO); //启用vao
            //纹理Activator
            renderCtx.textrueActivator.reset();
            //uniform 数据提交
            material.uploadUniform(renderCtx);
            //绘制贴屏片 后渲染效果
            gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);
            //绘制完毕清理状态
            this.closeGLState();
        }
        /** 收集相机的 RenderImage组件 */
        collectCamRenderImage(cam, renderImages) {
            //onRenderImage 收集
            renderImages.length = 0;
            const camGameObject = cam.entity;
            for (let ii = 0, len1 = camGameObject.componentCount; ii < len1; ii++) {
                let c = camGameObject.getComponent(ii);
                if (!c[FIREFLYX.I_ON_RENDER_IMAGE_FUN])
                    continue;
                //renderImage comp
                renderImages.push(c);
            }
        }
        render(renderCtx, cameras) {
            super.render(renderCtx, cameras);
            this._currRenderCtx = renderCtx;
            const rendererMap = renderCtx.rendererMap;
            if (!rendererMap) {
                console.warn(`rendererMap is null.`);
                return;
            }
            const gl = FIREFLYX.App.webgl2;
            //管线初始化设置
            //设置 材质ubo忽略 block（内建 部分 UBO需要设置为忽略）
            this._ignBlcokNames.forEach((str) => {
                renderCtx.ignoreInMaterialBlockMap[str] = true;
            });
            //内建uniform 绑定
            this.bindBuiltinUniforms(renderCtx, false);
            //
            const cpRendererIDs = this._cullPassRendererIDs;
            cpRendererIDs.length = 0;
            //灯光参数设置提交
            const lightUBO = renderCtx.lightCollector.ubo;
            renderCtx.lightCollector.refreshUboData();
            gl.bindBufferRange(gl.UNIFORM_BUFFER, FIREFLYX.UBOBindingEnum.LIGHT, lightUBO, 0, renderCtx.lightCollector.uboBlockSize);
            //天空盒设置
            if (renderCtx.skyboxMaterial) {
                const skyboxName = `u_skybox`;
                const skyboxTex = renderCtx.skyboxMaterial.getProperty(skyboxName);
                FIREFLYX.Shader.setProperty(skyboxName, skyboxTex);
                //skybox 强制开mipmap
                if (skyboxTex && !skyboxTex.paramSetting.mipmap) {
                    skyboxTex.paramSetting.mipmap = true;
                    skyboxTex.apply();
                }
            }
            //renderImage rt
            let srcRT = null;
            let destRT = null;
            //相机loop
            for (let i = 0, len = cameras.length; i < len; i++) {
                const cam = cameras[i];
                //相机初始化设置
                //使用相机
                this.useCamera(cam, renderCtx);
                let renderImages = this._renderImageComps;
                //onRenderImage 收集
                this.collectCamRenderImage(cam, renderImages);
                const needRenderImage = renderImages.length > 0;
                //屏幕颜色 纹理初始化
                if (needRenderImage && (!srcRT || !destRT)) {
                    srcRT = FIREFLYX.RenderTexture2D.GetTemporary();
                    srcRT.setFBTexEnable(true, true, true);
                    destRT = FIREFLYX.RenderTexture2D.GetTemporary();
                    destRT.setFBTexEnable(true, false, false);
                }
                //清理状态
                this._rendererMInfoMap.forEach((v, k) => { if (v)
                    RendererMaterialInfo.pool.delete(v); });
                this._rendererMInfoMap.clear();
                this._shadowCasterRList.length = 0;
                //相机 cull
                this.cameraFrustumCulling(cam, rendererMap, cpRendererIDs);
                //材质组预处理和分析
                this.materialGroupAnalyse(rendererMap, cpRendererIDs, this._rendererMInfoMap, this._shadowCasterRList);
                //是否使用 相机深度纹理
                let useCamDepthTex = this._shadowCasterRList.length > 0;
                if (useCamDepthTex) {
                    //相机深度图更新
                    this.renderCamDepthTex(renderCtx);
                }
                //渲染阴影生成通道
                this.renderShadowCasterPass(renderCtx);
                //前向渲染管线渲染
                if (this._drawDebugFrame) {
                    //渲染 debug帧
                    this.renderDebugFrame(renderCtx, this._drawDebugFrame);
                }
                else {
                    //renderImage 渲染前处理
                    if (needRenderImage && srcRT && destRT) {
                        this.renderImagePro(renderCtx, srcRT, destRT);
                    }
                    //相机帧缓冲清理
                    this.renderCameraFrameClear(cam, renderCtx);
                    //执行渲染pass
                    this.renderForwardPass(renderCtx, cam);
                    //绘制天空盒
                    if (cam.clearMode == FIREFLYX.CameraClearMode.SKY_BOX) {
                        this.renderSkyBox(renderCtx);
                    }
                }
                //后渲染到纹理
                if (needRenderImage && srcRT && destRT) {
                    this.renderImagePost(renderCtx, renderImages, srcRT, destRT);
                }
            }
            //管线清理
            FIREFLYX.RenderTexture2D.ReleaseTemporary(srcRT);
            FIREFLYX.RenderTexture2D.ReleaseTemporary(destRT);
            this.rtTemporaryRelease();
            //还原 材质ubo忽略 block 设置
            this._ignBlcokNames.forEach((str) => {
                renderCtx.ignoreInMaterialBlockMap[str] = false;
            });
            //内建uniform 绑定解除
            this.bindBuiltinUniforms(renderCtx, true);
            //final
            this._currRenderCtx = null;
        }
    }
    //渲染细节设定
    //渲染 Primitive:
    //1. 一个有效 渲染器 有 1 至 多个 渲染 Primitive
    //2. 每个 渲染primitive 渲染使用的 材质组 与 渲染器的材质组列表 顺序对齐。
    //3. 出现 Primitive 数大于 材质组的数量 ，用 最后的 材质组 填充对齐
    ForwardPipeline.HELP_VEC3 = new FIREFLYX.Vector3();
    ForwardPipeline.HELP_VEC3_1 = new FIREFLYX.Vector3();
    FIREFLYX.ForwardPipeline = ForwardPipeline;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="../Rendering/forwardPipeline.ts" />
/// <reference path="../Core/lightCollector.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /** 场景管理器 */
    class SceneManager {
        /** 渲染管线对象 */
        static get renderPipeline() { return this._renderPipeline; }
        static set renderPipeline(val) { this._renderPipeline = val; }
        /** 渲染上下文 */
        static get renderContext() { return this._renderContext; }
        /** 灯光收集器 */
        static get lightCollector() { return this._lightCollector; }
        static init(gl) {
            FIREFLYX.GameObject.init(this.onGameObjectMake.bind(this), this.onGameObjectDispose.bind(this), this.onGameObjectActiveInHChange.bind(this), this.onComponentAdd.bind(this), this.onComponentRemove.bind(this));
            this._renderPipeline = new FIREFLYX.ForwardPipeline();
            this._renderContext = new FIREFLYX.RenderContext(gl);
            this._lightCollector = new FIREFLYX.LightCollector();
            //设置 onRenderImage 函数
            this._onRenderImageFun = SceneManager.nodesOnRenderImage.bind(SceneManager);
            //组件 函数接口 容器设置
            this._compFunListMap.set(FIREFLYX.I_START_FUN, this._compStartFunMap);
            this._compFunListMap.set(FIREFLYX.I_UPDATE_FUN, this._compUpdateFunMap);
            this._compFunListMap.set(FIREFLYX.I_LATE_UPDATE_FUN, this._compLateUpdateFunMap);
            this._compFunListMap.set(FIREFLYX.I_ON_RENDER_IMAGE_FUN, this._compOnRenderImageFunMap);
            //事件注册
            FIREFLYX.GlobalEventManager.Instance.addListener("viweFrameChange", this.onViweFrameChange, this);
        }
        static update(dt) {
            //初始状态
            this.start();
            this.ckEnable();
            //第一次 update
            this.nodesUpdate(dt);
            //第二次 update
            this.nodesLateUpdate(dt);
            //渲染
            this.renderer();
        }
        /** 当输出画面框有改变 */
        static onViweFrameChange(e) {
            for (let i = 0, len = this._cameras.length; i < len; i++) {
                const cam = this._cameras[i];
                cam.reflashASP();
            }
        }
        /** 当实体被创建 */
        static onGameObjectMake(e) {
            //
        }
        /** 当实体被销毁 */
        static onGameObjectDispose(e) {
            //
        }
        /**
         * 组件与场景链接
         * @param c 组件
         * @param isLink    是否链接 , false 为断开
         */
        static componentLink(c, isLink) {
            //收集组件
            const cInstID = c.getInstanceID();
            const cFunsMap = this._compFunListMap;
            cFunsMap.forEach((v, k) => {
                if (c[k] != null) {
                    if (isLink) {
                        if (k == FIREFLYX.I_START_FUN && this._compStartSuccMap.get(cInstID))
                            return; //一次执行 标记过了的 跳过
                        v.set(cInstID, c);
                    }
                    else {
                        v.delete(cInstID);
                    }
                }
            });
        }
        /** 当实在层级体被激活状态改变 */
        static onGameObjectActiveInHChange(e) {
            const activeInHierarchy = e.activeInHierarchy;
            if (e.light) {
                if (activeInHierarchy) {
                    this._lightCollector.enableLight(e.light);
                }
                else {
                    this._lightCollector.disableLight(e.light);
                }
            }
            else if (e.camera) {
                const camera = e.camera;
                const idx = this._cameras.indexOf(camera);
                if (activeInHierarchy) {
                    if (idx == -1)
                        this._cameras.push(camera);
                }
                else {
                    if (idx != -1)
                        this._cameras.splice(idx, 1);
                }
            }
            else if (e.renderer) {
                const renderer = e.renderer;
                if (renderer) {
                    const instID = renderer.getInstanceID();
                    if (this._rendererMap.has(instID)) {
                        this._rendererMap.delete(instID);
                    }
                }
            }
            //组件link
            const compCount = e.componentCount;
            for (let i = 0; i < compCount; i++) {
                const comp = e.getComponent(i);
                if (!comp)
                    continue;
                this.componentLink(comp, activeInHierarchy);
            }
        }
        /** 当组件被添加 */
        static onComponentAdd(c) {
            if (!c.entity)
                return;
            const cProt = Object.getPrototypeOf(c);
            if (!cProt)
                return;
            const cType = cProt.constructor;
            const cInstID = c.getInstanceID();
            switch (cType.useSign) {
                case "light":
                    const light = c;
                    this._lightCollector.enableLight(light);
                    break;
                case "camera":
                    const camera = c;
                    if (this._cameras.indexOf(camera) == -1) {
                        this._cameras.push(camera);
                    }
                    break;
                case "renderer":
                    const instID = cInstID;
                    this._rendererMap.set(instID, c);
                    break;
                default:
            }
            //组件link
            if (!c.entity.activeInHierarchy)
                return; //active 状态为 false 不收集 
            this.componentLink(c, true);
        }
        /** 当组件被移除 */
        static onComponentRemove(c) {
            const cProt = Object.getPrototypeOf(c);
            if (!cProt)
                return;
            const cType = cProt.constructor;
            switch (cType.useSign) {
                case "light":
                    const light = c;
                    this._lightCollector.disableLight(light);
                    break;
                case "camera":
                    const camera = c;
                    const idx = this._cameras.indexOf(camera);
                    if (idx != -1)
                        this._cameras.splice(idx, 1);
                    break;
                case "renderer":
                    const renderer = c;
                    const instID = renderer.getInstanceID();
                    if (this._rendererMap.has(instID)) {
                        this._rendererMap.delete(instID);
                    }
                    break;
                default:
            }
            //组件link
            this.componentLink(c, false);
        }
        /** 场景的节点初开始触发 */
        static start() {
            if (this._compStartFunMap.size < 1)
                return;
            //遍历需要更新的节点列表
            this._compStartFunMap.forEach((v) => {
                v[FIREFLYX.I_START_FUN]();
                this._compStartSuccMap.set(v.getInstanceID(), true);
            });
            //start 执行完清理
            this._compStartFunMap.clear();
        }
        /** 场景的节点激活触发 */
        static ckEnable() {
            //
        }
        /** 场景节点的更新 */
        static nodesUpdate(dt) {
            //遍历需要更新的节点列表
            this._compUpdateFunMap.forEach((v) => {
                v[FIREFLYX.I_UPDATE_FUN](dt);
            });
            //event dispatch
            if (FIREFLYX.GlobalEventManager.Instance.listenerCount("sceneUpdate") > 0) {
                FIREFLYX.GlobalEventManager.Instance.dispatchEvent("sceneUpdate", dt);
            }
        }
        /** 场景节点的帧末更新 */
        static nodesLateUpdate(dt) {
            //遍历需要更新的节点列表
            this._compLateUpdateFunMap.forEach((v) => {
                v[FIREFLYX.I_LATE_UPDATE_FUN](dt);
            });
            //event dispatch
            if (FIREFLYX.GlobalEventManager.Instance.listenerCount("sceneLateUpdate") > 0) {
                FIREFLYX.GlobalEventManager.Instance.dispatchEvent("sceneLateUpdate", dt);
            }
        }
        /** 场景后渲染到纹理 */
        static nodesOnRenderImage(image) {
            //遍历需要渲染的节点列表
            this._compOnRenderImageFunMap.forEach((v) => {
                let src = null;
                let dest = null;
                v[FIREFLYX.I_ON_RENDER_IMAGE_FUN](src, dest);
            });
        }
        /** 场景的渲染 */
        static renderer() {
            let cams = this._cameras;
            if (cams.length < 1) {
                return;
            }
            let rPl = this._renderPipeline;
            if (!rPl) {
                console.warn(`render fial, renderPipeline is null.`);
                return;
            }
            //renderContext 准备数据
            let rCtx = this.renderContext;
            rCtx.rendererMap = this._rendererMap;
            rCtx.lightCollector = this._lightCollector;
            if (this._compOnRenderImageFunMap.size > 0) {
                rCtx.onRenderImageFun = this._onRenderImageFun;
            }
            //开始 render
            rPl.render(rCtx, cams);
            //render 结束
            rCtx.onRenderImageFun = null;
            //RenderTexture2D 临时对象清理，避免用户没有回收
            FIREFLYX.RenderTexture2D.ReleaseTemporaryAll();
        }
    }
    SceneManager._cameras = [];
    SceneManager._rendererMap = new Map();
    SceneManager._compStartSuccMap = new Map();
    SceneManager._compStartFunMap = new Map();
    SceneManager._compUpdateFunMap = new Map();
    SceneManager._compLateUpdateFunMap = new Map();
    SceneManager._compOnRenderImageFunMap = new Map();
    SceneManager._compFunListMap = new Map();
    FIREFLYX.SceneManager = SceneManager;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 输出画面分辨率自适应模式 */
    let ResolutionAdaptationMode;
    (function (ResolutionAdaptationMode) {
        /** 按实际拉伸 */
        ResolutionAdaptationMode[ResolutionAdaptationMode["REALITY"] = 0] = "REALITY";
        /** 固定分辨宽，高度自适应 */
        ResolutionAdaptationMode[ResolutionAdaptationMode["FIXED_WIDTH"] = 1] = "FIXED_WIDTH";
        /** 固定分辨高，高度自适应*/
        ResolutionAdaptationMode[ResolutionAdaptationMode["FIXED_HEIGHT"] = 2] = "FIXED_HEIGHT";
    })(ResolutionAdaptationMode = FIREFLYX.ResolutionAdaptationMode || (FIREFLYX.ResolutionAdaptationMode = {}));
    /** 画面方向旋转模式 */
    let AspectRotateMode;
    (function (AspectRotateMode) {
        /** 不做旋转操作 */
        AspectRotateMode[AspectRotateMode["NONE"] = 0] = "NONE";
        /** 旋转保持竖方向（高度 大于 宽度） */
        AspectRotateMode[AspectRotateMode["PORTRAIT"] = 1] = "PORTRAIT";
        /** 旋转保持横方向（宽度 大于 高度） */
        AspectRotateMode[AspectRotateMode["LANDSCAPE"] = 2] = "LANDSCAPE";
    })(AspectRotateMode = FIREFLYX.AspectRotateMode || (FIREFLYX.AspectRotateMode = {}));
    /**
     * 画面方位朝向 管理器
     */
    class ViewAspectManager {
        /** 输出画面分辨率自适应模式 */
        static get resolutionAdapMode() { return this._resolutionAdapMode; }
        static set resolutionAdapMode(val) {
            if (this._resolutionAdapMode == val)
                return;
            this._resolutionAdapMode = val;
            this.resolutionAdaptation();
        }
        /** 输出画面方向的旋转模式 */
        static get aspectRotateMode() { return this._aspectRotateMode; }
        static set aspectRotateMode(val) {
            if (this._aspectRotateMode == val)
                return;
            this._aspectRotateMode = val;
            this.aspectRotateAdaptation();
            this.resolutionAdaptation();
        }
        /** 使用设备像素比率修正分辨率 */
        static get useDevicePixelRatioFix() { return this._useDevicePixelRatioFix; }
        static set useDevicePixelRatioFix(val) {
            if (this._useDevicePixelRatioFix == val)
                return;
            this._useDevicePixelRatioFix = val;
            this.resolutionAdaptation();
        }
        /** 固定分辨率设定值 */
        static get fixedResolution() { return this._fixedResolution; }
        static set fixedResolution(val) {
            const tarVal = isNaN(val) || val < 1 ? 1 : val;
            if (this._fixedResolution == tarVal)
                return;
            this._fixedResolution = tarVal;
            if (this._resolutionAdapMode == ResolutionAdaptationMode.FIXED_WIDTH ||
                this._resolutionAdapMode == ResolutionAdaptationMode.FIXED_HEIGHT) {
                this.resolutionAdaptation();
            }
        }
        /** 初始化 */
        static init(element) {
            if (this._inited)
                return;
            if (!element) {
                console.error(`element is null.`);
                return;
            }
            //
            let canvasEle = null;
            if (element instanceof HTMLCanvasElement) {
                canvasEle = element;
            }
            else {
                this._externalDIV = element;
                this._externalDIV;
                //是一个DIV ，自己创建一个 canvas 元素
            }
            this.canvasElement = canvasEle;
            //end
            this._inited = true;
        }
        /** 更新 */
        static update() {
            //
            if (this.autoRefresh) {
                this.checkContainerResize();
            }
        }
        /** 执行分辨自适应调整 */
        static resolutionAdaptation() {
            const canvas = this.canvasElement;
            const parent = canvas.parentElement;
            if (!parent)
                return;
            const whReverse = this._canvasRotation != 0;
            let w = whReverse ? parent.clientHeight : parent.clientWidth;
            let h = whReverse ? parent.clientWidth : parent.clientHeight;
            //style clientSize 、top 、 left同步
            let top = 0;
            let left = 0;
            let defHalf = Math.abs(w - h) * 0.5;
            switch (this._canvasRotation) {
                case 90:
                    top = defHalf;
                    left = -defHalf;
                    break;
                case -90:
                    top = -defHalf;
                    left = defHalf;
                    break;
                default:
            }
            //style
            canvas.style.position = "absolute";
            canvas.style.width = `${w}px`;
            canvas.style.height = `${h}px`;
            canvas.style.top = `${top}px`;
            canvas.style.left = `${left}px`;
            let fixPixelRatio = !this._useDevicePixelRatioFix ? 1 : devicePixelRatio || 1;
            const maxW = w * fixPixelRatio;
            const maxH = h * fixPixelRatio;
            //调整 canvas画布实际分辨率尺寸
            //canvas.width 和 canvas.height 是实际分辨率
            //canvas.clientWidth、canvas.clientHeight 为 html元素 尺寸
            switch (this._resolutionAdapMode) {
                case ResolutionAdaptationMode.REALITY:
                    canvas.width = maxW;
                    canvas.height = maxH;
                    break;
                case ResolutionAdaptationMode.FIXED_WIDTH:
                    canvas.width = this._fixedResolution > maxW ? maxW : this._fixedResolution;
                    canvas.height = canvas.width * (h / w);
                    break;
                case ResolutionAdaptationMode.FIXED_HEIGHT:
                    canvas.height = this._fixedResolution > maxH ? maxH : this._fixedResolution;
                    canvas.width = canvas.height * (w / h);
                    break;
                default:
            }
            //抛事件
            FIREFLYX.GlobalEventManager.Instance.dispatchEvent("viweFrameChange", "resize");
            this._lastWidth = w;
            this._lastHeight = h;
        }
        /** 执行画面方向调整 */
        static aspectRotateAdaptation() {
            const canvas = this.canvasElement;
            const parent = canvas.parentElement;
            if (!parent)
                return;
            //计算旋转
            let tarRotate = this.calcAspectRotation();
            if (tarRotate == this._aspectRotateMode)
                return;
            //调整 css style ，让canvas旋转
            canvas.style.transform = `rotate(${tarRotate}deg)`;
            this._canvasRotation = tarRotate;
        }
        /** 计算方向旋转角度 */
        static calcAspectRotation() {
            const canvas = this.canvasElement;
            const parent = canvas.parentElement;
            if (!parent)
                return 0;
            let tarRotate = 0;
            //判断是否需要 旋转操作
            switch (this._aspectRotateMode) {
                case AspectRotateMode.NONE:
                    tarRotate = 0;
                    break;
                case AspectRotateMode.LANDSCAPE:
                    tarRotate = parent.clientHeight > parent.clientWidth ? 90 : 0;
                    break;
                case AspectRotateMode.PORTRAIT:
                    tarRotate = parent.clientWidth > parent.clientHeight ? -90 : 0;
                    break;
                default:
            }
            return tarRotate;
        }
        /** 检查容器尺寸变化 */
        static checkContainerResize() {
            const canvas = this.canvasElement;
            const parent = canvas.parentElement;
            if (!parent)
                return;
            const whReverse = this._canvasRotation != 0;
            let w = whReverse ? parent.clientHeight : parent.clientWidth;
            let h = whReverse ? parent.clientWidth : parent.clientHeight;
            //方向旋转 自适应
            this.aspectRotateAdaptation();
            //检查是否 有变化
            let isSizeDirty = this._lastWidth != w || this._lastHeight != h;
            if (!isSizeDirty)
                return;
            this.resolutionAdaptation();
        }
    }
    ViewAspectManager._inited = false;
    ViewAspectManager._fixedResolution = 1080;
    ViewAspectManager._resolutionAdapMode = ResolutionAdaptationMode.REALITY;
    ViewAspectManager._aspectRotateMode = AspectRotateMode.NONE;
    ViewAspectManager._useDevicePixelRatioFix = true;
    ViewAspectManager._lastWidth = 0;
    ViewAspectManager._lastHeight = 0;
    ViewAspectManager._canvasRotation = 0;
    /** 自动在每次updata 检查 分辨率自适应 和 方向 */
    ViewAspectManager.autoRefresh = true;
    FIREFLYX.ViewAspectManager = ViewAspectManager;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./renderPipeline.ts" />
/// <reference path="./renderContext.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 引擎内建 延迟渲染 管线
     */
    class DeferredPipeline extends FIREFLYX.RenderPipeline {
        render(context, cameras) {
            super.render(context, cameras);
            console.log(`DeferredPipeline render`);
        }
    }
    FIREFLYX.DeferredPipeline = DeferredPipeline;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** */
    class Graphics {
        static init() {
            //blit material
            const sh = FIREFLYX.DefaultAsset.GetShader(FIREFLYX.BUILTIN_SHADER_POSTEFF_IMAGE);
            this._blitMtr = new FIREFLYX.Material("blitMtr");
            this._blitMtr.shader = sh;
        }
        /**
         * 绘制一个全屏的 quad
         * @param material 绘制的材质
         * @param viewPortRect 绘制设置的viewport
         */
        static DrawFullQuad(material, viewPortRect) {
            const renderCtx = FIREFLYX.SceneManager.renderContext;
            const gl = renderCtx.webgl2;
            const quadMesh = FIREFLYX.DefaultAsset.GetMesh(FIREFLYX.DefMesh.QUAD_FULL_CLIP);
            const sh = material.shader;
            const cSH = sh.getCompiled(material.getKeywordID());
            const sp = cSH.program;
            //viewport set
            gl.viewport(viewPortRect.x, viewPortRect.y, viewPortRect.w, viewPortRect.h);
            //pass clear
            gl.clearColor(0, 0, 0, 0); //backclear color
            gl.clearDepth(1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
            //WebGLProgram use
            gl.useProgram(sp);
            //vao
            gl.bindVertexArray(quadMesh.VAO); //启用vao
            //uniform 数据提交
            material.uploadUniform(renderCtx);
            //纹理Activator
            renderCtx.textrueActivator.reset();
            //绘制贴屏片 后渲染效果
            gl.drawElements(gl.TRIANGLES, quadMesh.elementLenght, quadMesh.elementType, 0);
            //绘制完毕清理状态
            gl.bindVertexArray(null);
        }
        /**
         * 绘制到纹理
         */
        static DrawTexture() {
            //
        }
        /**
         * 使用着色器将源纹理复制到目标渲染纹理中（主要用于后处理效果）
         * @param src 源纹理
         * @param dest 目标渲染纹理
         * @param material 渲染材质
         */
        static Blit(src, dest, material) {
            //
            if (!src || !dest)
                return;
            const mtr = material ? material : this._blitMtr;
            const gl = FIREFLYX.App.webgl2;
            const rect = this.help_Rect;
            //材质设置
            mtr.setProperty("u_mainTex", src);
            //设置 viewport size
            rect.set(0, 0, dest.width, dest.height); //绘制viewport 对齐 dest的大小
            //enable fbo
            gl.bindFramebuffer(gl.FRAMEBUFFER, dest.fbo);
            //绘制
            this.DrawFullQuad(mtr, rect);
            //清理状态
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        }
    }
    Graphics.help_Rect = new FIREFLYX.Rect(0, 0, 1, 1);
    FIREFLYX.Graphics = Graphics;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 灯光阴影 */
    class LightShadow {
        constructor() {
            /** 基本偏移 */
            this.bias = 0.05;
            /** 法线方向偏移 */
            this.normalBias = 0.4;
        }
    }
    FIREFLYX.LightShadow = LightShadow;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * webgl 纹理激活器
     */
    class TextureActivator {
        constructor(gl) {
            this._gl = gl;
            this._currUse = 0;
            this._currActID = 0;
            this._maxCount = FIREFLYX.WebglCapability.MAX_COMBINED_TEXTURE_IMAGE_UNITS;
        }
        /** 当前设备支持的shader纹理数最大数量 */
        get maxCount() { return this._maxCount; }
        /** 当前激活的纹理槽位ID */
        get currActiveTexID() { return this._currActID; }
        /** 激活一个纹理槽位 */
        activeTexOnce() {
            const gl = this._gl;
            const texID = this._currUse;
            if (texID >= this._maxCount)
                return false;
            this._currActID = texID;
            gl.activeTexture(gl[`TEXTURE${texID}`]);
            this._currUse++;
            return true;
        }
        /** 重置 */
        reset() { this._currUse = 0; }
    }
    FIREFLYX.TextureActivator = TextureActivator;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** webgl 能力 */
    class WebglCapability {
        //--------------------------------------
        /** 初始化 */
        static init(gl) {
            //
            this._webgl2 = gl;
            // eslint-disable-next-line no-empty
            if (this._webgl2) { }
            //设备相关能力限值
            this.MAX_COMBINED_TEXTURE_IMAGE_UNITS = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
            this.MAX_UNIFORM_BLOCK_SIZE = gl.getParameter(gl.MAX_UNIFORM_BLOCK_SIZE); //单个UBO 最大byte
            this.UNIFORM_BUFFER_OFFSET_ALIGNMENT = gl.getParameter(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT); //
            this.MAX_VERTEX_UNIFORM_BLOCKS = gl.getParameter(gl.MAX_VERTEX_UNIFORM_BLOCKS);
            this.MAX_FRAGMENT_UNIFORM_BLOCKS = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_BLOCKS);
            this.MAX_COMBINED_UNIFORM_BLOCKS = gl.getParameter(gl.MAX_COMBINED_UNIFORM_BLOCKS);
            //拓展相关
            this.EXT_texture_filter_anisotropic = gl.getExtension("EXT_texture_filter_anisotropic") ||
                gl.getExtension("MOZ_EXT_texture_filter_anisotropic") ||
                gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic");
        }
    }
    FIREFLYX.WebglCapability = WebglCapability;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class Script extends FIREFLYX.Component {
        constructor() {
            super(...arguments);
            this._enabled = true;
        }
        get enabled() { return this._enabled; }
        set enabled(val) { this._enabled = val; }
    }
    FIREFLYX.Script = Script;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./script.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    class CameraHoverScript extends FIREFLYX.Script {
        constructor() {
            super(...arguments);
            this._mouseDown = false;
            this._panAngle = 0;
            this._panRad = 0;
            this._tiltAngle = 0;
            this._tiltRad = 0;
            /** 观察点 */
            this.lookAtPoint = new FIREFLYX.Vector3(0, 0, 0);
            /** 观察距离 */
            this.distance = 30;
            this.minPanAngle = -Infinity;
            this.maxPanAngle = Infinity;
            this.minTileAngle = -89;
            this.maxTileAngle = 89;
            this.scaleSpeed = 2;
            this.cupTargetV3 = new FIREFLYX.Vector3();
        }
        /** 平移角度 */
        set panAngle(value) {
            this._panAngle = Math.max(this.minPanAngle, Math.min(this.maxPanAngle, value));
            this._panRad = this._panAngle * Math.PI / 180;
        }
        get panAngle() {
            return this._panAngle;
        }
        /** 仰角角度 */
        set tiltAngle(value) {
            this._tiltAngle = Math.max(this.minTileAngle, Math.min(this.maxTileAngle, value));
            this._tiltRad = this._tiltAngle * Math.PI / 180;
        }
        get tiltAngle() {
            return this._tiltAngle;
        }
        start() {
            //事件注册
            FIREFLYX.GlobalEventManager.Instance.addListener("pointDown", this.onPointDown, this);
            FIREFLYX.GlobalEventManager.Instance.addListener("pointUp", this.onPointUp, this);
            FIREFLYX.GlobalEventManager.Instance.addListener("pointMove", this.onPointMove, this);
            FIREFLYX.GlobalEventManager.Instance.addListener("mousewheel", this.onWheel, this);
        }
        update(delta) {
            let distanceX = this.distance * Math.sin(this._panRad) * Math.cos(this._tiltRad);
            let distanceY = this.distance * (this._tiltRad == 0 ? 0 : Math.sin(this._tiltRad));
            let distanceZ = this.distance * Math.cos(this._panRad) * Math.cos(this._tiltRad);
            if (this.lookAtTarget) {
                this.cupTargetV3.copyFrom(this.lookAtTarget.position);
                // gd3d.math.vec3Clone(this.lookAtTarget.getWorldTranslate(),this.cupTargetV3);
            }
            else {
                this.cupTargetV3.copyFrom(this.lookAtPoint);
            }
            //更新位置
            if (!this.entity)
                return;
            let eyeTran = this.entity.transform;
            let ePos = eyeTran.position;
            ePos.set(this.cupTargetV3.x + distanceX, this.cupTargetV3.y + distanceY, this.cupTargetV3.z + distanceZ);
            eyeTran.position = ePos;
            //看向目标
            this.entity.transform.lookatPoint(this.cupTargetV3);
        }
        onPointDown(e) {
            this._mouseDown = true;
            this._lastMouseX = e.x;
            this._lastMouseY = e.y;
        }
        onPointUp() {
            this._mouseDown = false;
        }
        onPointMove(e) {
            if (!this._mouseDown)
                return;
            let moveX = e.x - this._lastMouseX;
            let moveY = e.y - this._lastMouseY;
            this.panAngle += moveX;
            this.tiltAngle += moveY;
            this._lastMouseX = e.x;
            this._lastMouseY = e.y;
        }
        onWheel(e) {
            this.distance = Math.max(this.distance - this.getDetal(e) * this.scaleSpeed, 1);
        }
        getDetal(ev) {
            let delta = 0;
            if (ev.detail) {
                delta = -1 * ev.detail;
            }
            else if (ev.wheelDelta) {
                delta = ev.wheelDelta / 120;
            }
            else if (ev.DOM_DELTA_PIXEL) {
                delta = ev.DOM_DELTA_PIXEL / 120;
            }
            return delta;
        }
        remove() {
            //事件注销
            FIREFLYX.GlobalEventManager.Instance.removeListener("pointDown", this.onPointDown, this);
            FIREFLYX.GlobalEventManager.Instance.removeListener("pointUp", this.onPointUp, this);
            FIREFLYX.GlobalEventManager.Instance.removeListener("pointMove", this.onPointMove, this);
            FIREFLYX.GlobalEventManager.Instance.removeListener("mousewheel", this.onWheel, this);
        }
    }
    FIREFLYX.CameraHoverScript = CameraHoverScript;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 实体原始类型 */
    let GameObjectPrimitiveType;
    (function (GameObjectPrimitiveType) {
        /** 正方体 */
        GameObjectPrimitiveType[GameObjectPrimitiveType["CUBE"] = 0] = "CUBE";
        /** 正四边形 */
        GameObjectPrimitiveType[GameObjectPrimitiveType["QUAD"] = 1] = "QUAD";
        /** 正球体 */
        GameObjectPrimitiveType[GameObjectPrimitiveType["SPHERE"] = 2] = "SPHERE";
        /** 圆柱体体 */
        GameObjectPrimitiveType[GameObjectPrimitiveType["CYLINDER"] = 3] = "CYLINDER";
    })(GameObjectPrimitiveType = FIREFLYX.GameObjectPrimitiveType || (FIREFLYX.GameObjectPrimitiveType = {}));
    class GameObjectUtil {
        /**
         * 创建一个原始样式实体
         * @param pType 原始样式类型
         */
        static createPrimitive(pType) {
            let mType = FIREFLYX.DefMesh.CUBE;
            //初始化资源
            switch (pType) {
                case GameObjectPrimitiveType.CUBE:
                    mType = FIREFLYX.DefMesh.CUBE;
                    break;
                case GameObjectPrimitiveType.QUAD:
                    mType = FIREFLYX.DefMesh.QUAD;
                    break;
                case GameObjectPrimitiveType.SPHERE:
                    mType = FIREFLYX.DefMesh.SPHERE;
                    break;
                case GameObjectPrimitiveType.CYLINDER:
                    mType = FIREFLYX.DefMesh.CYLINDER;
                    break;
                default:
            }
            let m = FIREFLYX.DefaultAsset.GetMesh(mType);
            const result = new FIREFLYX.GameObject(FIREFLYX.DefMesh[mType]);
            const mr = result.addComponent(FIREFLYX.MeshRenderer);
            mr.mesh = m;
            mr.materialGroup[0] = FIREFLYX.DefaultAsset.GetMaterial(FIREFLYX.DefMaterial.MODEL);
            mr.materialGroup[1] = FIREFLYX.DefaultAsset.GetMaterial(FIREFLYX.DefMaterial.SHADOW_MAP);
            return result;
        }
    }
    FIREFLYX.GameObjectUtil = GameObjectUtil;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class ShaderUtil {
        /**
         * 设置 材质的属性值, 用于对应shader unifrom 全局字段
         * @param name 属性名字
         * @param value 值
         * @returns 设置是否成功
         */
        static SetProperty(propertyMap, name, value) {
            if (!name) {
                console.log(`setProperty invalid , property name is null.`);
                return false;
            }
            if (value == propertyMap[name]) {
                return false;
            }
            if (value == null) {
                delete propertyMap[name];
                return false;
            }
            //是否是 materialValue[]?
            if (!Array.isArray(value)) {
                propertyMap[name] = this.GetPropertyUseValue(value);
            }
            else {
                let len = value.length;
                let arr = propertyMap[name];
                if (arr == null) {
                    arr = propertyMap[name] = [];
                }
                arr.length = len;
                for (let i = 0; i < len; i++) {
                    const subVal = value[i];
                    arr[i] = this.GetPropertyUseValue(subVal);
                }
            }
            return true;
        }
        static GetPropertyUseValue(val) {
            if (typeof (val) == `object`) {
                if (val.byteLength != null) {
                    return val;
                }
                else if (val.asArray != null) {
                    const arr = val.asArray();
                    return Array.isArray(arr) ? new Float32Array(arr) : arr;
                }
                //纹理
                return val;
            }
            //is number
            return new Float32Array([val]);
        }
    }
    FIREFLYX.ShaderUtil = ShaderUtil;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** glsl 库 color */
    FIREFLYX.DefaultAsset.glslLibCodeMap.common = `
//common lib
`;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class AssetBundleProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.ASSET_BUNDLE;
            this.suffix = ".ab.zip";
            this.rawType = "blob";
        }
        parse(data) {
            //throw new Error("Method not implemented.");
            return null;
        }
    }
    FIREFLYX.AssetBundleProcessor = AssetBundleProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 基础image（.jpg .png .web） parse
     */
    class BaseImageParse {
        static parse(data, filePath) {
            let tex = new FIREFLYX.Texture2D();
            let flipY = true;
            let p = FIREFLYX.ResPathProcessorParameterMap.get(filePath);
            if (p) {
                flipY = p.flipY ? true : false;
            }
            //纹理储存设置
            tex.paramSetting.flipY = flipY;
            tex.setTextureData(data);
            //参数设置
            //应用参数
            tex.apply();
            return tex;
        }
    }
    FIREFLYX.BaseImageParse = BaseImageParse;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class BinProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.BIN;
            this.suffix = ".bin";
            this.rawType = "arraybuffer";
        }
        parse(data) {
            let bin = new FIREFLYX.BinaryData();
            bin.buffer = data;
            return bin;
        }
    }
    FIREFLYX.BinProcessor = BinProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class GLBProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.GLB;
            this.suffix = ".glb";
            this.rawType = "arraybuffer";
        }
        parse(data, filePath) {
            //解析GLB 
            const HEADER_MAGIC = "glTF";
            const HEADER_LENGTH = 12;
            const CHUNK_TYPES_BIN = 0x004E4942;
            const CHUNK_TYPES_JSON = 0x4E4F534A;
            const headerView = new DataView(data, 0, HEADER_LENGTH);
            const header = {
                magic: FIREFLYX.decodeUtf8(new Uint8Array(data.slice(0, 4))),
                version: headerView.getUint32(4, true),
                length: headerView.getUint32(8, true)
            };
            //检查 data 是否是有效的 GLB 格式 ，版本是否支持
            if (header.magic !== HEADER_MAGIC) {
                throw new Error(`Unsupported glTF-Binary header. `);
            }
            else if (header.version < 2.0) {
                throw new Error(`Legacy binary file detected.`);
            }
            //分解块数据
            const chunkContentsLength = header.length - HEADER_LENGTH;
            const chunkView = new DataView(data, HEADER_LENGTH);
            let chunkIndex = 0;
            let gltfJsonText = "";
            let binData = null;
            while (chunkIndex < chunkContentsLength) {
                const chunkLength = chunkView.getUint32(chunkIndex, true);
                chunkIndex += 4;
                const chunkType = chunkView.getUint32(chunkIndex, true);
                chunkIndex += 4;
                if (chunkType === CHUNK_TYPES_JSON) {
                    const contentArray = new Uint8Array(data, HEADER_LENGTH + chunkIndex, chunkLength);
                    gltfJsonText = FIREFLYX.decodeUtf8(contentArray);
                }
                else if (chunkType === CHUNK_TYPES_BIN) {
                    const byteOffset = HEADER_LENGTH + chunkIndex;
                    binData = data.slice(byteOffset, byteOffset + chunkLength);
                } // Clients must ignore chunks with unknown types.
                chunkIndex += chunkLength;
            }
            //解析path
            const _idx = filePath.lastIndexOf("/");
            const filename = filePath.substring(_idx + 1);
            const gltfPath = filePath.substring(0, _idx + 1);
            //保存bin 到AssetManager
            const binName = `${FIREFLYX.GLTF.BIN_TAG}0`;
            const bin = new FIREFLYX.BinaryData(binName);
            bin.buffer = binData;
            const bufPath = `${gltfPath}${binName}`;
            FIREFLYX.AssetManager.SetAsset(bufPath, bin);
            //gltfJSON 数据解析
            const gltfJson = JSON.parse(gltfJsonText);
            //gltf 资源
            const gltf = new FIREFLYX.GLTF(filename);
            gltf.setData(gltfJson, gltfPath);
            return gltf;
        }
    }
    FIREFLYX.GLBProcessor = GLBProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class GLSLProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.GLSL;
            this.suffix = ".glsl";
            this.rawType = "text";
        }
        parse(data, filePath) {
            let glsl = new FIREFLYX.GLSL();
            glsl.filePath = filePath;
            glsl.setCode(data);
            return glsl;
        }
    }
    FIREFLYX.GLSLProcessor = GLSLProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class GLTFProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.GLTF;
            this.suffix = ".gltf";
            this.rawType = "json";
        }
        parse(data, filePath) {
            let gltf = new FIREFLYX.GLTF();
            let gltfPath = filePath.substring(0, filePath.lastIndexOf("/") + 1);
            gltf.setData(data, gltfPath);
            return gltf;
        }
    }
    FIREFLYX.GLTFProcessor = GLTFProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 原生 html 图像 Processor
     */
    class HtmlImageProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.HTML_IMAGE;
            this.suffix = ".hi.*"; //htmlImageElement
            this.rawType = "blob";
        }
        parse(data, filePath) {
            let hImage = new FIREFLYX.HtmlImage();
            hImage.image = data;
            return hImage;
        }
    }
    FIREFLYX.HtmlImageProcessor = HtmlImageProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class JpgTexProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.TEXTURE;
            this.suffix = ".jpg";
            this.rawType = "blob";
        }
        parse(data, filePath) {
            return FIREFLYX.BaseImageParse.parse(data, filePath);
        }
    }
    FIREFLYX.JpgTexProcessor = JpgTexProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class MaterialProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.MATERIAL;
            this.suffix = ".material.json";
            this.rawType = "json";
        }
        parse(data) {
            //throw new Error("Method not implemented.");
            return null;
        }
    }
    FIREFLYX.MaterialProcessor = MaterialProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class OBJMeshProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.OBJ_MESH;
            this.rawType = "text";
            this.suffix = ".obj";
        }
        parse(data) {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j;
            let text = data;
            //构建mesh
            let mData = new FIREFLYX.MeshData();
            let poss = [];
            let uvs = [];
            let normals = [];
            let triindexs = [];
            let mesh = new FIREFLYX.Mesh();
            mesh.data = mData;
            //逐行读取 文本
            let vn = [];
            let vt = [];
            let f = [];
            //fun 1
            let i = 0;
            let _t = text.replace("\r", "\n");
            let lines = _t.split("\n");
            while (i < lines.length) {
                let str = lines[i];
                i++;
                if (str.length < 1 || str[0] == "#")
                    continue;
                let vals = str.split(" ");
                let vArr = [];
                vals.forEach((s) => { if (s.length > 0)
                    vArr.push(s); });
                if (vArr.length < 1)
                    continue;
                switch (vArr[0]) {
                    case "o":
                        mesh.name = (_a = vArr[1]) !== null && _a !== void 0 ? _a : "OBJMesh";
                        break;
                    case "v":
                        let x = (_b = Number(vArr[1])) !== null && _b !== void 0 ? _b : 0;
                        let y = (_c = Number(vArr[2])) !== null && _c !== void 0 ? _c : 0;
                        let z = (_d = Number(vArr[3])) !== null && _d !== void 0 ? _d : 0;
                        poss.push(x * -1, y, z);
                        break;
                    case "vn":
                        vn.push((_e = Number(vArr[1])) !== null && _e !== void 0 ? _e : 0, (_f = Number(vArr[2])) !== null && _f !== void 0 ? _f : 0, (_g = Number(vArr[3])) !== null && _g !== void 0 ? _g : 0);
                        break;
                    case "vt":
                        vt.push((_h = Number(vArr[1])) !== null && _h !== void 0 ? _h : 0, (_j = Number(vArr[2])) !== null && _j !== void 0 ? _j : 0);
                        break;
                    case "f":
                        if (vArr[4] == null) {
                            f.push(vArr[1], vArr[2], vArr[3]);
                        }
                        else {
                            f.push(vArr[1], vArr[2], vArr[4]);
                            f.push(vArr[2], vArr[3], vArr[4]);
                        }
                        break;
                    default:
                }
            }
            //顶点长度确定
            let pointCount = poss.length / 3;
            uvs.length = pointCount * 2;
            normals.length = pointCount * 3;
            let initVMap = {};
            //整理填充meshdata
            for (let j = 0, len = f.length; j < len; j++) {
                let fArr = f[j].split("/");
                let vi = Number(fArr[0]) - 1;
                if (!initVMap[vi]) {
                    initVMap[vi] = true;
                    let ti = 2 * vi;
                    let ni = 3 * vi;
                    let _ti = 2 * (Number(fArr[1]) - 1);
                    let _ni = 3 * (Number(fArr[2]) - 1);
                    uvs[ti] = vt[_ti];
                    uvs[ti + 1] = vt[_ti + 1];
                    normals[ni] = vn[_ni];
                    normals[ni + 1] = vn[_ni + 1];
                    normals[ni + 2] = vn[_ni + 2];
                }
                triindexs.push(vi);
            }
            //
            mData.posArr = new Float32Array(poss);
            mData.uvArr = new Float32Array(uvs);
            mData.normalArr = new Float32Array(normals);
            let burClass = FIREFLYX.MeshData.GetTriIndexBufferTypeByPosLen(poss.length / 3);
            mData.triIndexArr = new burClass(triindexs);
            mData.calcPosBoundary();
            if (mData.triIndexArr.length > 0) {
                mesh.apply();
            }
            return mesh;
        }
    }
    FIREFLYX.OBJMeshProcessor = OBJMeshProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class PngTexProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.TEXTURE;
            this.suffix = ".png";
            this.rawType = "blob";
        }
        parse(data, filePath) {
            return FIREFLYX.BaseImageParse.parse(data, filePath);
        }
    }
    FIREFLYX.PngTexProcessor = PngTexProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class ShaderProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.SHADER;
            this.suffix = ".shader.json";
            this.rawType = "json";
        }
        parse(data, filePath) {
            var _a, _b, _c, _d;
            let shader = new FIREFLYX.Shader();
            shader.filePath = filePath;
            shader.tag = (_a = data.tag) !== null && _a !== void 0 ? _a : "";
            shader.lightMode = (_b = data.lightMode) !== null && _b !== void 0 ? _b : "";
            shader.vertexFile = (_c = data.vs) !== null && _c !== void 0 ? _c : "";
            shader.fragmentFile = (_d = data.fs) !== null && _d !== void 0 ? _d : "";
            //属性
            let property = data.property;
            if (property) {
                shader.definePropertyMap = {};
                let dpMap = shader.definePropertyMap;
                const keys = Object.keys(property);
                for (let i = 0, len = keys.length; i < len; i++) {
                    const k = keys[i];
                    let p = property[k];
                    dpMap[k] = {
                        type: p.type,
                        value: p.value
                    };
                }
            }
            //shader JIT 编译模式，不需要预先编译组合设置了 
            // //变体关键字
            // //多编译
            // let multiCompiles = data.multi_compiles as string[][];
            // if (multiCompiles) {
            //     shader.multiCompiles = [];
            //     for (let i = 0, len = multiCompiles.length; i < len; i++) {
            //         const m = multiCompiles[i];
            //         shader.multiCompiles.push(m.concat());
            //     }
            // }
            return shader;
        }
    }
    FIREFLYX.ShaderProcessor = ShaderProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    class WebpTexProcessor {
        constructor() {
            this.Type = FIREFLYX.AssetType.TEXTURE;
            this.suffix = ".webp";
            this.rawType = "blob";
        }
        parse(data, filePath) {
            return FIREFLYX.BaseImageParse.parse(data, filePath);
        }
    }
    FIREFLYX.WebpTexProcessor = WebpTexProcessor;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 引擎纹理基类 */
    class TextureBase extends FIREFLYX.Asset {
        /** webgl 纹理对象 */
        get glTextrue() { return this._glTextrue; }
        /** 提交使用当前参数设置 */
        apply() {
            //
        }
        dispose() {
            super.dispose();
            //清理
            if (!this._glTextrue)
                return;
            let gl = FIREFLYX.App.webgl2;
            gl.deleteTexture(this._glTextrue);
            this._glTextrue = null;
        }
    }
    FIREFLYX.TextureBase = TextureBase;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./textureBase.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 渲染纹理,能接收渲染输出覆盖当前纹理像素
     */
    class RenderTexture2D extends FIREFLYX.TextureBase {
        /**
         * 实例化一个RenderTexture2D
         * @param name 资源名
         * @param color 开启颜色
         * @param depth 开启深度
         * @param stencil 开启模板
         */
        constructor(name = "", color = true, depth = false, stencil = false) {
            super(name);
            this._width = 256;
            this._height = 256;
            const gl = FIREFLYX.App.webgl2;
            //fbo
            this._fbo = gl.createFramebuffer();
            //texture attachment set
            this.setFBTexEnable(color, depth, stencil);
        }
        /** 纹理像素宽 */
        get width() { return this._width; }
        /** 纹理像素高 */
        get height() { return this._height; }
        /** 颜色纹理参数设置 */
        get paramSetting() { return this._paramSetting; }
        /** 深度与模板纹理参数设置 */
        get depthStencilParamSetting() { return this._dsParamSetting; }
        /** 启用了颜色 */
        get isEnableColor() { return this._enableColor; }
        /** 启用了深度 */
        get isEnableDepth() { return this._enableDepth; }
        /** 启用了模板 */
        get isEnableStencil() { return this._enableStencil; }
        /** webgl 深度和模板纹理 */
        get depthStencilTexture() { return this._depthStencilTexture; }
        /** fbo 对象 */
        get fbo() { return this._fbo; }
        /** 应用尺寸设置 */
        applySize() {
            // let w = this._width;
            // let h = this._height;
            const gl = FIREFLYX.App.webgl2;
            //颜色纹理
            let w = this._enableColor ? this._width : 1; //size 设成1 减少内存占用？
            let h = this._enableColor ? this._height : 1;
            const colorParam = this._paramSetting;
            if (this._glTextrue && colorParam) {
                gl.bindTexture(gl.TEXTURE_2D, this._glTextrue);
                gl.texImage2D(gl.TEXTURE_2D, 0, colorParam.format, w, h, 0, colorParam.getGLFormat(), colorParam.texelType, null);
            }
            //深度和模板纹理
            const enableSDTex = this._enableDepth || this._enableStencil;
            w = enableSDTex ? this._width : 1;
            h = enableSDTex ? this._height : 1;
            const depthParam = this._dsParamSetting;
            if (this._depthStencilTexture && depthParam) {
                gl.bindTexture(gl.TEXTURE_2D, this._depthStencilTexture.glTextrue);
                gl.texImage2D(gl.TEXTURE_2D, 0, depthParam.format, w, h, 0, depthParam.getGLFormat(), depthParam.texelType, null);
            }
            //清理纹理绑定
            gl.bindTexture(gl.TEXTURE_2D, null);
        }
        /**
         * 设置FramebufferTexture 类型的开启
         * @param color 开启颜色
         * @param depth 开启深度
         * @param stencil 开启模板
         */
        setFBTexEnable(color = true, depth = false, stencil = false) {
            if (this.isDisposed)
                return;
            const colorDirty = this._enableColor != color;
            const dsDirty = this._enableDepth != depth || this._enableStencil != stencil;
            if (!colorDirty && !dsDirty)
                return;
            const gl = FIREFLYX.App.webgl2;
            const fbo = this._fbo;
            if (!fbo)
                return;
            const lastDepth = this._enableDepth;
            const lastStencil = this._enableStencil;
            this._enableColor = color;
            this._enableDepth = depth;
            this._enableStencil = stencil;
            const enableDS = depth || stencil; //开启 Stencil Depth
            let needApply = false;
            gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
            //color set ATTACHMENT
            if (colorDirty) {
                if (!this._glTextrue) {
                    this._glTextrue = gl.createTexture();
                    this._paramSetting = new FIREFLYX.TextureParamSetting();
                    needApply = true;
                }
                const glColorTex = color ? this._glTextrue : null;
                gl.bindTexture(gl.TEXTURE_2D, glColorTex);
                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glColorTex, 0);
            }
            //depth stencil set ATTACHMENT
            if (dsDirty) {
                if (enableDS) {
                    if (!this._depthStencilTexture) {
                        this._depthStencilTexture = new FIREFLYX.TextureBase(`depthStencil`);
                        this._depthStencilTexture._glTextrue = gl.createTexture();
                        this._dsParamSetting = new FIREFLYX.TextureParamSetting(gl.FLOAT, FIREFLYX.TextureFormat.DEPTH_COMPONENT32F, false, false, false, FIREFLYX.TexWrapMode.CLAMP, FIREFLYX.TexWrapMode.CLAMP);
                        needApply = true;
                    }
                }
                const dsTex = this._depthStencilTexture;
                const dsParamSetting = this._dsParamSetting;
                if (dsTex && dsParamSetting) {
                    const _glDSTex = enableDS ? dsTex.glTextrue : null;
                    const s = enableDS ? stencil : lastStencil;
                    const d = enableDS ? depth : lastDepth;
                    gl.bindTexture(gl.TEXTURE_2D, _glDSTex);
                    let format;
                    let texelType;
                    if (s && d) {
                        texelType = gl.FLOAT_32_UNSIGNED_INT_24_8_REV;
                        format = FIREFLYX.TextureFormat.DEPTH32F_STENCIL8;
                        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, _glDSTex, 0);
                    }
                    else if (d) {
                        texelType = gl.FLOAT;
                        format = FIREFLYX.TextureFormat.DEPTH_COMPONENT32F;
                        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, _glDSTex, 0);
                    }
                    else {
                        texelType = gl.UNSIGNED_BYTE;
                        format = FIREFLYX.TextureFormat.STENCIL_INDEX8;
                        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.TEXTURE_2D, _glDSTex, 0);
                    }
                    if (enableDS && dsParamSetting.format != format || dsParamSetting.texelType != texelType) {
                        dsParamSetting.format = format;
                        dsParamSetting.texelType = texelType;
                        needApply = true;
                    }
                }
            }
            needApply = true;
            //applay
            if (needApply) {
                this.apply();
            }
            this.applySize();
            //检查 FramebufferStatus 
            const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
            if (status !== gl.FRAMEBUFFER_COMPLETE) {
                console.error(`Framebuffer is incomplete: status = ${status}`);
            }
            //set null
            gl.bindTexture(gl.TEXTURE_2D, null);
            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        }
        /**
         * 获取临时 renderTextrue2D 对象
         * @returns renderTextrue2D 对象
         */
        static GetTemporary() {
            let rt = this._pool.new();
            this._rtTemporaryMap.set(rt.UUID, rt);
            return rt;
        }
        /**
         * 释放临时RenderTexture2D 对象
         * @param rt RenderTexture2D对象
         */
        static ReleaseTemporary(rt) {
            if (!rt)
                return;
            this._pool.delete(rt);
            this._rtTemporaryMap.delete(rt.UUID);
        }
        /**
         * 释放所有 临时RenderTexture2D 对象
         */
        static ReleaseTemporaryAll() {
            this._rtTemporaryMap.forEach((v) => {
                if (v)
                    this._pool.delete(v);
            });
            this._rtTemporaryMap.clear();
        }
        apply() {
            let gl = FIREFLYX.App.webgl2;
            if (this._enableColor) {
                FIREFLYX.TextureParamSetting.applyTex(gl.TEXTURE_2D, this._glTextrue, this._paramSetting);
            }
            if (this._depthStencilTexture && this._dsParamSetting) {
                FIREFLYX.TextureParamSetting.applyTex(gl.TEXTURE_2D, this._depthStencilTexture.glTextrue, this._dsParamSetting);
            }
        }
        dispose() {
            super.dispose();
            //
            if (this._depthStencilTexture) {
                this._depthStencilTexture.dispose();
                this._depthStencilTexture = null;
            }
            if (this._fbo) {
                let gl = FIREFLYX.App.webgl2;
                gl.deleteFramebuffer(this._fbo);
                this._fbo = null;
            }
            this._dsParamSetting = null;
            this._paramSetting = null;
        }
        /**
         * 设置纹理尺寸
         * @param w 纹理宽度
         * @param h 纹理高度
         */
        setSize(w, h) {
            if (this._width == w && this._height == h)
                return;
            this._width = w;
            this._height = h;
            this.applySize();
        }
    }
    RenderTexture2D._pool = new FIREFLYX.Pool(RenderTexture2D, 0);
    RenderTexture2D._rtTemporaryMap = new Map();
    FIREFLYX.RenderTexture2D = RenderTexture2D;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./textureBase.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 2D纹理
     */
    class Texture2D extends FIREFLYX.TextureBase {
        constructor(name = "", uuid) {
            super(name, uuid);
            this._width = 0;
            this._height = 0;
            const gl = FIREFLYX.App.webgl2;
            // this.texelType = gl.UNSIGNED_BYTE;
            let glTex = gl.createTexture();
            if (glTex != null)
                this._glTextrue = glTex;
            this._paramSetting = new FIREFLYX.TextureParamSetting();
        }
        /** 参数设置 */
        get paramSetting() { return this._paramSetting; }
        /** 纹理宽度 */
        get width() { return this._width; }
        /** 纹理高度 */
        get height() { return this._height; }
        apply() {
            const gl = FIREFLYX.App.webgl2;
            FIREFLYX.TextureParamSetting.applyTex(gl.TEXTURE_2D, this._glTextrue, this._paramSetting);
        }
        setTextureData(imgData, width, height) {
            var _a, _b;
            if (!imgData)
                return;
            const gl = FIREFLYX.App.webgl2;
            const param = this._paramSetting;
            let oldUPA = gl.getParameter(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL);
            let oldUFY = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);
            //绑定指定纹理对象
            gl.bindTexture(gl.TEXTURE_2D, this._glTextrue);
            //像素存储参数设置
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, param.premultiplyAlpha);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, param.flipY);
            let glFormat = param.getGLFormat();
            //上传纹理数据
            if (width == null || height == null) {
                let image = imgData;
                gl.texImage2D(gl.TEXTURE_2D, 0, param.format, glFormat, param.texelType, image);
                this._width = (_a = image.width) !== null && _a !== void 0 ? _a : 1;
                this._height = (_b = image.height) !== null && _b !== void 0 ? _b : 1;
            }
            else {
                gl.texImage2D(gl.TEXTURE_2D, 0, param.format, width, height, 0, glFormat, param.texelType, imgData);
                this._width = width;
                this._height = height;
            }
            //还原存储参数设置
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, oldUPA);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, oldUFY);
            //解除绑定
            gl.bindTexture(gl.TEXTURE_2D, null);
        }
        dispose() {
            super.dispose();
            this._paramSetting = null;
        }
    }
    FIREFLYX.Texture2D = Texture2D;
})(FIREFLYX || (FIREFLYX = {}));
/// <reference path="./textureBase.ts" />
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * 2D纹理 数组
     */
    class Texture2DArray extends FIREFLYX.TextureBase {
        constructor(name = "", uuid) {
            super(name, uuid);
            this._width = 0;
            this._height = 0;
            this._length = 0;
            const gl = FIREFLYX.App.webgl2;
            // this.texelType = gl.UNSIGNED_BYTE;
            let glTex = gl.createTexture();
            if (glTex != null)
                this._glTextrue = glTex;
            this._paramSetting = new FIREFLYX.TextureParamSetting();
        }
        /** 参数设置 */
        get paramSetting() { return this._paramSetting; }
        /** 纹理宽度 */
        get width() { return this._width; }
        /** 纹理高度 */
        get height() { return this._height; }
        /** 数组长度 */
        get length() { return this._length; }
        apply() {
            const gl = FIREFLYX.App.webgl2;
            FIREFLYX.TextureParamSetting.applyTex(gl.TEXTURE_2D_ARRAY, this._glTextrue, this._paramSetting);
        }
        setTextureData(imgDatas, width, height) {
            var _a, _b, _c, _d;
            if (!imgDatas || imgDatas.length < 1)
                return;
            const gl = FIREFLYX.App.webgl2;
            const param = this._paramSetting;
            let oldUPA = gl.getParameter(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL);
            let oldUFY = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);
            //绑定指定纹理对象
            gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._glTextrue);
            //像素存储参数设置
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, param.premultiplyAlpha);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, param.flipY);
            const texLen = imgDatas.length;
            //
            let glFormat = param.getGLFormat();
            //上传纹理数据
            if (width == null || height == null) {
                let image = imgDatas[0];
                this._width = (_a = image.width) !== null && _a !== void 0 ? _a : 1;
                this._height = (_b = image.height) !== null && _b !== void 0 ? _b : 1;
            }
            else {
                this._width = width;
                this._height = height;
            }
            //
            gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, param.format, this._width, this._height, texLen, 0, glFormat, param.texelType, null);
            //填充subtex
            for (let i = 0; i < texLen; i++) {
                const tex = imgDatas[i];
                const w = width == null ? (_c = tex.width) !== null && _c !== void 0 ? _c : 1 : width;
                const h = height == null ? (_d = tex.height) !== null && _d !== void 0 ? _d : 1 : height;
                gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, i, w, h, 1, glFormat, param.texelType, tex);
            }
            //还原存储参数设置
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, oldUPA);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, oldUFY);
            //解除绑定
            gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
        }
        setSubTextureData(index, imgData) {
            if (isNaN(index) || index < 0 || index > this._length)
                return;
            //
            const gl = FIREFLYX.App.webgl2;
            const param = this._paramSetting;
            let oldUPA = gl.getParameter(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL);
            let oldUFY = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);
            //绑定指定纹理对象
            gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._glTextrue);
            //像素存储参数设置
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, param.premultiplyAlpha);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, param.flipY);
            //
            let glFormat = param.getGLFormat();
            //设置subtex
            gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, index, this._width, this._height, 1, glFormat, param.texelType, imgData);
            //还原存储参数设置
            gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, oldUPA);
            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, oldUFY);
            //解除绑定
            gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
        }
    }
    FIREFLYX.Texture2DArray = Texture2DArray;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /**
     * cube纹理 的面
     */
    let TextureCubeFace;
    (function (TextureCubeFace) {
        /** X 正面 */
        TextureCubeFace[TextureCubeFace["POSITIVE_X"] = WebGL2RenderingContext.TEXTURE_CUBE_MAP_POSITIVE_X] = "POSITIVE_X";
        /** X 反面 */
        TextureCubeFace[TextureCubeFace["NEGATIVE_X"] = WebGL2RenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_X] = "NEGATIVE_X";
        /** Y 正面 */
        TextureCubeFace[TextureCubeFace["POSITIVE_Y"] = WebGL2RenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Y] = "POSITIVE_Y";
        /** Y 反面 */
        TextureCubeFace[TextureCubeFace["NEGATIVE_Y"] = WebGL2RenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Y] = "NEGATIVE_Y";
        /** Z 正面 */
        TextureCubeFace[TextureCubeFace["POSITIVE_Z"] = WebGL2RenderingContext.TEXTURE_CUBE_MAP_POSITIVE_Z] = "POSITIVE_Z";
        /** Z 反面 */
        TextureCubeFace[TextureCubeFace["NEGATIVE_Z"] = WebGL2RenderingContext.TEXTURE_CUBE_MAP_NEGATIVE_Z] = "NEGATIVE_Z";
    })(TextureCubeFace = FIREFLYX.TextureCubeFace || (FIREFLYX.TextureCubeFace = {}));
    /**
     * cube 纹理
     */
    class TextureCube extends FIREFLYX.TextureBase {
        constructor(name = "", uuid) {
            super(name, uuid);
            this._sizeMap = {
                [TextureCubeFace.POSITIVE_X]: [1, 1],
                [TextureCubeFace.NEGATIVE_X]: [1, 1],
                [TextureCubeFace.POSITIVE_Y]: [1, 1],
                [TextureCubeFace.NEGATIVE_Y]: [1, 1],
                [TextureCubeFace.POSITIVE_Z]: [1, 1],
                [TextureCubeFace.NEGATIVE_Z]: [1, 1],
            };
            const gl = FIREFLYX.App.webgl2;
            // this.texelType = gl.UNSIGNED_BYTE;
            let glTex = gl.createTexture();
            if (glTex != null)
                this._glTextrue = glTex;
            this._paramSetting = new FIREFLYX.TextureParamSetting();
        }
        /** 参数设置 */
        get paramSetting() { return this._paramSetting; }
        apply() {
            const gl = FIREFLYX.App.webgl2;
            FIREFLYX.TextureParamSetting.applyTex(gl.TEXTURE_CUBE_MAP, this._glTextrue, this._paramSetting);
        }
        /**
         * 获取 指定面 纹理的宽度
         * @param face cube指定面
         * @returns 纹理的宽度
         */
        getWidth(face) {
            return this._sizeMap[face][0];
        }
        /**
         * 获取 指定面 纹理的高度
         * @param face cube指定面
         * @returns 纹理的高度
         */
        getHeight(face) {
            return this._sizeMap[face][1];
        }
        setTextureData(imgData, face, width, height) {
            var _a, _b;
            if (!imgData)
                return;
            const gl = FIREFLYX.App.webgl2;
            const param = this._paramSetting;
            // let oldUPA = gl.getParameter(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL) as boolean;
            // let oldUFY = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL) as boolean;
            //绑定指定纹理对象
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, this._glTextrue);
            // //像素存储参数设置
            // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, param.premultiplyAlpha);
            // gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, param.flipY);
            let glFormat = param.getGLFormat();
            const size = this._sizeMap[face];
            //上传纹理数据
            if (width == null || height == null) {
                let image = imgData;
                gl.texImage2D(face, 0, param.format, glFormat, param.texelType, image);
                size[0] = (_a = image.width) !== null && _a !== void 0 ? _a : 1;
                size[1] = (_b = image.height) !== null && _b !== void 0 ? _b : 1;
            }
            else {
                gl.texImage2D(face, 0, param.format, width, height, 0, glFormat, param.texelType, imgData);
                size[0] = width;
                size[1] = height;
            }
            // //还原存储参数设置
            // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, oldUPA);
            // gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, oldUFY);
            //解除绑定
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
        }
        setTextureAllData(imgDatas, width, height) {
            if (!imgDatas) {
                console.error(`imgData is null`);
                return;
            }
            const imgsLen = imgDatas.length;
            const len = imgsLen < 0 ? 0 : imgsLen > 6 ? 6 : imgsLen;
            for (let i = 0; i < len; i++) {
                const face = TextureCube._faceList[i];
                const imgData = imgDatas[i];
                if (width == null || height == null) {
                    this.setTextureData(imgData, face);
                }
                else {
                    this.setTextureData(imgData, face, width, height);
                }
            }
        }
        dispose() {
            super.dispose();
            const len = TextureCube._faceList.length;
            for (let i = 0; i < len; i++) {
                const k = TextureCube._faceList[i];
                delete this._sizeMap[k];
            }
            this._sizeMap = null;
            this._paramSetting = null;
        }
    }
    TextureCube._faceList = [
        TextureCubeFace.POSITIVE_X,
        TextureCubeFace.NEGATIVE_X,
        TextureCubeFace.POSITIVE_Y,
        TextureCubeFace.NEGATIVE_Y,
        TextureCubeFace.POSITIVE_Z,
        TextureCubeFace.NEGATIVE_Z,
    ];
    FIREFLYX.TextureCube = TextureCube;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 纹理UV超出处理模式 */
    let TexWrapMode;
    (function (TexWrapMode) {
        TexWrapMode[TexWrapMode["REPEAT"] = 0] = "REPEAT";
        TexWrapMode[TexWrapMode["CLAMP"] = 1] = "CLAMP";
        TexWrapMode[TexWrapMode["MIRRORED"] = 2] = "MIRRORED";
    })(TexWrapMode = FIREFLYX.TexWrapMode || (FIREFLYX.TexWrapMode = {}));
    /** 各项异性 等级 */
    let AnisotropicLevel;
    (function (AnisotropicLevel) {
        AnisotropicLevel[AnisotropicLevel["OFF"] = 1] = "OFF";
        AnisotropicLevel[AnisotropicLevel["X2"] = 2] = "X2";
        AnisotropicLevel[AnisotropicLevel["X4"] = 4] = "X4";
        AnisotropicLevel[AnisotropicLevel["X8"] = 8] = "X8";
        AnisotropicLevel[AnisotropicLevel["X16"] = 16] = "X16";
    })(AnisotropicLevel = FIREFLYX.AnisotropicLevel || (FIREFLYX.AnisotropicLevel = {}));
    /** 纹理格式 (对齐webgl internalformat )*/
    let TextureFormat;
    (function (TextureFormat) {
        TextureFormat[TextureFormat["RGBA"] = WebGL2RenderingContext.RGBA] = "RGBA";
        TextureFormat[TextureFormat["RGB"] = WebGL2RenderingContext.RGB] = "RGB";
        TextureFormat[TextureFormat["LUMINANCE_ALPHA"] = WebGL2RenderingContext.LUMINANCE_ALPHA] = "LUMINANCE_ALPHA";
        TextureFormat[TextureFormat["LUMINANCE"] = WebGL2RenderingContext.LUMINANCE] = "LUMINANCE";
        TextureFormat[TextureFormat["ALPHA"] = WebGL2RenderingContext.ALPHA] = "ALPHA";
        TextureFormat[TextureFormat["DEPTH_COMPONENT32F"] = WebGL2RenderingContext.DEPTH_COMPONENT32F] = "DEPTH_COMPONENT32F";
        TextureFormat[TextureFormat["DEPTH_COMPONENT24"] = WebGL2RenderingContext.DEPTH_COMPONENT24] = "DEPTH_COMPONENT24";
        TextureFormat[TextureFormat["DEPTH_COMPONENT16"] = WebGL2RenderingContext.DEPTH_COMPONENT16] = "DEPTH_COMPONENT16";
        TextureFormat[TextureFormat["DEPTH24_STENCIL8"] = WebGL2RenderingContext.DEPTH24_STENCIL8] = "DEPTH24_STENCIL8";
        TextureFormat[TextureFormat["DEPTH32F_STENCIL8"] = WebGL2RenderingContext.DEPTH32F_STENCIL8] = "DEPTH32F_STENCIL8";
        TextureFormat[TextureFormat["STENCIL_INDEX8"] = WebGL2RenderingContext.STENCIL_INDEX8] = "STENCIL_INDEX8";
        TextureFormat[TextureFormat["RGBA4"] = WebGL2RenderingContext.RGBA4] = "RGBA4";
        TextureFormat[TextureFormat["RGB5_A1"] = WebGL2RenderingContext.RGB5_A1] = "RGB5_A1";
        TextureFormat[TextureFormat["RGB10_A2"] = WebGL2RenderingContext.RGB10_A2] = "RGB10_A2";
        TextureFormat[TextureFormat["RGBA16F"] = WebGL2RenderingContext.RGBA16F] = "RGBA16F";
        TextureFormat[TextureFormat["RGBA32F"] = WebGL2RenderingContext.RGBA32F] = "RGBA32F";
        TextureFormat[TextureFormat["RGB565"] = WebGL2RenderingContext.RGB565] = "RGB565";
        TextureFormat[TextureFormat["RGB16F"] = WebGL2RenderingContext.RGB16F] = "RGB16F";
        TextureFormat[TextureFormat["RGB32F"] = WebGL2RenderingContext.RGB32F] = "RGB32F";
        TextureFormat[TextureFormat["R16F"] = WebGL2RenderingContext.R16F] = "R16F";
        TextureFormat[TextureFormat["R32F"] = WebGL2RenderingContext.R32F] = "R32F";
    })(TextureFormat = FIREFLYX.TextureFormat || (FIREFLYX.TextureFormat = {}));
    /**
     * 纹理 参数设置
     */
    class TextureParamSetting {
        // /** 各项异性滤波级别 */
        // public anisoLevel: number = 0;
        /**
         * 构造 纹理 参数设置
         * @param texelType 纹素的数据类型 ,默认值 UNSIGNED_BYTE (可能的值 UNSIGNED_BYTE 、FLOAT)
         * @param format 纹理格式 ,默认值 RGBA
         * @param linear 线性滤波 ,默认值 true
         * @param mipmap 开启mipmap ,默认值 false
         * @param warpU 纹理U轴 超出处理模式 ,默认值 REPEAT
         * @param warpV 纹理V轴 超出处理模式 ,默认值 REPEAT
         * @param flipY 纹理Y轴翻转 ,默认值 false
         * @param premultiplyAlpha 纹理预乘透明值 ,默认值 false
         * @param mipmapBaseLevel mipmap 最小开始级别 ,默认值 0
         * @param mipmapMaxLevel mipmap 最大开始级别 ,默认值 9
         */
        constructor(texelType = WebGL2RenderingContext.UNSIGNED_BYTE, format = TextureFormat.RGBA, linear = true, linearMipmap = true, mipmap = false, warpU = TexWrapMode.REPEAT, warpV = TexWrapMode.REPEAT, flipY = false, premultiplyAlpha = false, mipmapBaseLevel = 0, mipmapMaxLevel = 9, anisotropic = AnisotropicLevel.OFF) {
            this.texelType = texelType;
            this.format = format;
            this.linear = linear;
            this.linearMipmap = linearMipmap;
            this.mipmap = mipmap;
            this.warpU = warpU;
            this.warpV = warpV;
            this.flipY = flipY;
            this.premultiplyAlpha = premultiplyAlpha;
            this.mipmapBaseLevel = mipmapBaseLevel;
            this.mipmapMaxLevel = mipmapMaxLevel;
            this.anisotropic = anisotropic;
        }
        /**
         * 获取webgl 格式
         * @param glFormat webgl格式数据
         */
        getGLFormat() {
            const gl = FIREFLYX.App.webgl2;
            let format;
            switch (this.format) {
                case TextureFormat.RGB565:
                case TextureFormat.RGB16F:
                case TextureFormat.RGB32F:
                case TextureFormat.RGB:
                    format = gl.RGB;
                    break;
                case TextureFormat.LUMINANCE_ALPHA:
                    format = gl.LUMINANCE_ALPHA;
                    break;
                case TextureFormat.ALPHA:
                    format = gl.ALPHA;
                    break;
                case TextureFormat.LUMINANCE:
                    format = gl.LUMINANCE;
                    break;
                case TextureFormat.R16F:
                case TextureFormat.R32F:
                    format = gl.RED;
                    break;
                case TextureFormat.DEPTH_COMPONENT16:
                case TextureFormat.DEPTH_COMPONENT24:
                case TextureFormat.DEPTH_COMPONENT32F:
                    format = gl.DEPTH_COMPONENT;
                    break;
                case TextureFormat.DEPTH24_STENCIL8:
                case TextureFormat.DEPTH32F_STENCIL8:
                    format = gl.DEPTH_STENCIL;
                    break;
                case TextureFormat.STENCIL_INDEX8:
                    format = gl.STENCIL;
                    break;
                default: format = gl.RGBA;
            }
            return format;
        }
        /**
         * 获取webgl warp 值
         * @param mode TexWrapMode
         */
        static getGLWRAP(mode) {
            let gl = FIREFLYX.App.webgl2;
            switch (mode) {
                case TexWrapMode.REPEAT: return gl.REPEAT;
                case TexWrapMode.CLAMP: return gl.CLAMP_TO_EDGE;
                case TexWrapMode.MIRRORED: return gl.MIRRORED_REPEAT;
                default: return gl.CLAMP_TO_EDGE;
            }
        }
        /**
         * webgl纹理 TEXTURE_2D 应用参数设置
         * @param texTarget
         * @param glTexture
         * @param texParam
         */
        static apply2DBase(texTarget, glTexture, texParam) {
            let gl = FIREFLYX.App.webgl2;
            //绑定指定纹理对象
            gl.bindTexture(texTarget, glTexture);
            //gen mipmap
            if (texParam.mipmap) {
                gl.generateMipmap(texTarget);
            }
            //滤波设置
            let magFilter = texParam.linear ? gl.LINEAR : gl.NEAREST;
            let minFilter;
            if (texParam.mipmap) {
                if (texParam.linearMipmap) {
                    minFilter = texParam.linear ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_LINEAR;
                }
                else {
                    minFilter = texParam.linear ? gl.LINEAR_MIPMAP_NEAREST : gl.NEAREST_MIPMAP_NEAREST;
                }
            }
            else {
                minFilter = (texParam.linear ? gl.LINEAR : gl.NEAREST);
            }
            gl.texParameteri(texTarget, gl.TEXTURE_MAG_FILTER, magFilter);
            gl.texParameteri(texTarget, gl.TEXTURE_MIN_FILTER, minFilter);
            //各项异性
            const anisotropicExt = FIREFLYX.WebglCapability.EXT_texture_filter_anisotropic;
            if (anisotropicExt) {
                const max = gl.getParameter(anisotropicExt.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
                const val = isNaN(texParam.anisotropic) || texParam.anisotropic < 1 ? AnisotropicLevel.OFF : texParam.anisotropic;
                gl.texParameterf(texTarget, anisotropicExt.TEXTURE_MAX_ANISOTROPY_EXT, val > max ? max : val);
            }
            //纹理UV超出处理
            let wrapS = this.getGLWRAP(texParam.warpU);
            let wrapT = this.getGLWRAP(texParam.warpV);
            gl.texParameteri(texTarget, gl.TEXTURE_WRAP_S, wrapS);
            gl.texParameteri(texTarget, gl.TEXTURE_WRAP_T, wrapT);
            //解除绑定
            gl.bindTexture(texTarget, null);
        }
        /**
         * webgl纹理 应用参数设置
         * @param texTarget 纹理target  (可能的值 TEXTURE_2D、TEXTURE_CUBE_MAP、TEXTURE_3D、TEXTURE_2D_ARRAY)
         * @param glTexture webgl纹理对象
         * @param texParam 纹理参数设置
         */
        static applyTex(texTarget, glTexture, texParam) {
            switch (texTarget) {
                case WebGL2RenderingContext.TEXTURE_2D_ARRAY:
                case WebGL2RenderingContext.TEXTURE_CUBE_MAP:
                case WebGL2RenderingContext.TEXTURE_2D:
                    this.apply2DBase(texTarget, glTexture, texParam);
                    break;
                default:
            }
        }
    }
    FIREFLYX.TextureParamSetting = TextureParamSetting;
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 普通深度 着色器*/
    FIREFLYX.BUILTIN_SHADER_BASE_DEPTH = `BaseDepth`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_BASE_DEPTH] = () => {
        const vsCode = `#version 300 es
        precision mediump float;
        layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
        uniform ubo_model_block{
            mat4 u_mMtx;                //模型矩阵
        } uboModel;

        uniform mat4 u_viewProjMtx;

        void main() {
            gl_Position = u_viewProjMtx * uboModel.u_mMtx * vec4(a_Position.xyz,1.0);
        }
        `;
        const fsCode = `#version 300 es
        void main(){
            //only depth
         }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_BASE_DEPTH;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 基础阴影图 着色器*/
    FIREFLYX.BUILTIN_SHADER_BASE_SHADOW_MAP = `ShadowMap`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_BASE_SHADOW_MAP] = () => {
        const vsCode = `#version 300 es
        precision mediump float;
        layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
        layout (location = 4) in vec4 a_Color0;   //顶点 色
        layout (location = 3) in vec2 a_UV;       //UV 纹理坐标

        out vec2 v_uv;
        out vec4 v_color;
        uniform ubo_model_block{
            mat4 u_mMtx;                //模型矩阵
        } uboModel;

        uniform mat4 u_viewProjMtx;

        void main() {
            v_uv = a_UV;
            v_color = a_Color0;
            gl_Position = u_viewProjMtx * uboModel.u_mMtx * vec4(a_Position.xyz,1.0);
        }
        `;
        const fsCode = `#version 300 es
        precision mediump float;

        uniform sampler2D u_mainTex;
        uniform vec4 u_mainColor;           //主颜色 颜色

        uniform vec2 u_resolution;              //画布分辨率

        in vec2 v_uv;
        in vec4 v_color;

        void main(){
            //only depth
            float density = 256.0;
            float minGap = 4.0;
            float a = v_color.a * texture(u_mainTex , v_uv).a * u_mainColor.a;  //获取 alpha 值
            float rate = 1.0 -a;    //进度 0-1
            float w = u_resolution.x;
            float h = u_resolution.y;
            float asp = w / h;
            float gapW = w / density;
            gapW = step(minGap , gapW) * gapW + step(gapW, minGap) * minGap;
            float gapH = gapW * asp;
            float valW = gapW * rate;
            float valH = gapH * rate;
            if(mod(gl_FragCoord.x , gapW) < valW || mod(gl_FragCoord.y , gapH) < valH) discard;     //按alpha 度进行镂空处理
        }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_BASE_SHADOW_MAP;
        result.lightMode = FIREFLYX.RenderPipeline.LM_SHADOW_CASTER;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 凹凸 + 镜面高光 着色器*/
    FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR = `BumpedSpecular`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR] = () => {
        //code 0 计算 级联阴影
        const vsCode0 = `#version 300 es
        precision mediump float;
        layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
        layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
        layout (location = 1) in vec3 a_Normal;   //法线 向量
        uniform ubo_model_block{
            mat4 u_mMtx;                //模型矩阵
        } uboModel;
        uniform ubo_cam_block{
            mat4 u_viewMtx;             //视窗矩阵
            mat4 u_projectMtx;          //投影矩阵
            vec4 u_eyePos;              //眼睛位置
        } uboCam;

        uniform mat4 u_lightVPMatrix_0;   //转到 主光源VP空间矩阵
        uniform mat4 u_lightVPMatrix_1;   //转到 主光源VP空间矩阵
        uniform mat4 u_lightVPMatrix_2;   //转到 主光源VP空间矩阵
        uniform mat4 u_lightVPMatrix_3;   //转到 主光源VP空间矩阵
        uniform vec4 u_csm_split;

        out vec3 v_wPos;
        out vec2 v_uv;
        out vec3 v_wNormal;
        out vec4[4] v_wPosLightSpaces;
        out float v_zDLin;  //0-1 范围z 的距离值
        void main() {
            v_uv = a_UV;
            v_wNormal = mat3(transpose(inverse(uboModel.u_mMtx))) * a_Normal;    //逆转置矩阵,可以让法向量在非统一缩放后保持准确,不受干扰。
            vec4 mPos = vec4(a_Position.xyz,1.0);
            v_wPos = (uboModel.u_mMtx * mPos).xyz;
            // mat4 mvpMat = u_projectMtx * u_viewMtx * uboModel.u_mMtx;
            mat4 mvpMat = uboCam.u_projectMtx * uboCam.u_viewMtx * uboModel.u_mMtx;
            gl_Position = mvpMat * a_Position;
            vec4 wPosV4 = vec4(v_wPos.xyz, 1.0);
            vec4 vPos = uboCam.u_viewMtx * wPosV4;
            float shadowDistance = u_csm_split[3];
            v_zDLin = vPos.z / shadowDistance;                                  //范围映射到  0-1
            v_wPosLightSpaces[0] = u_lightVPMatrix_0 * wPosV4;
            v_wPosLightSpaces[1] = u_lightVPMatrix_1 * wPosV4;
            v_wPosLightSpaces[2] = u_lightVPMatrix_2 * wPosV4;
            v_wPosLightSpaces[3] = u_lightVPMatrix_3 * wPosV4;
        }
        `;
        const fsCode0 = `#version 300 es
        precision mediump float;
        uniform sampler2D u_mainTex;
        uniform sampler2D u_mainShadowMap_0;
        uniform sampler2D u_mainShadowMap_1;
        uniform sampler2D u_mainShadowMap_2;
        uniform sampler2D u_mainShadowMap_3;
        uniform vec4 u_csm_split;
        uniform vec4 u_mainColor;       //主颜色 颜色

        uniform ubo_cam_block{
            mat4 u_viewMtx;                 //视窗矩阵
            mat4 u_projectMtx;              //投影矩阵
            vec4 u_eyePos;                  //眼睛位置
        } uboCam;

        uniform ubo_light_block{
            uniform vec4 _mainLightDir;
            uniform vec4 _mainLightColor;
            uniform vec4[64] _addLightsPos;
            uniform vec4[64] _addLightsColor;
            uniform vec4[64] _addLightsAtt;
            uniform vec4[64] _addLightsSpotDir;
        } uboLight;
        
        uniform ubo_material_block{
            uniform float u_spotSpec;       //镜面反射光斑
        } uboMaterial;

        in vec3 v_wPos;
        in vec2 v_uv;
        in vec3 v_wNormal;
        in vec4[4] v_wPosLightSpaces;
        in float v_zDLin;
        out vec4 color;

        float ShadowCalculation(vec4 wPosLightSpace , sampler2D shadowMap , float NoL)
        {
            // 执行透视除法
            vec3 projCoords = wPosLightSpace.xyz / wPosLightSpace.w;
            // 变换到[0,1]的范围
            projCoords = projCoords * 0.5 + 0.5;
            // 取得当前片段在光源视角下的深度
            float currentDepth = projCoords.z;
            //调节 阴影失真(Shadow Acne) 问题使用 阴影偏移
            float baseBias = 0.01;
            // float bias = max(baseBias * (1.0 - NoL), baseBias * 0.1);  //自适应处理
            float bias = baseBias; 
            
            bool isPCF = true;
            
            float shadow = 0.0;
            if(!isPCF){
                // 取得最近点的深度(使用[0,1]范围下的wPosLightSpace当坐标)
                float closestDepth = texture(shadowMap, projCoords.xy).r;
                // 检查当前片段是否在阴影中
                shadow = step(closestDepth , currentDepth - bias);
            }else{
                vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0));
                for(int x = -1; x <= 1; ++x)
                {
                    for(int y = -1; y <= 1; ++y)
                    {
                        float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; 
                        //shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;        
                        shadow += step(pcfDepth , currentDepth - bias);       
                    }    
                }
                shadow /= 9.0;
            }

            //超出 置 0处理 ,解决黑影错误问题
            float shadowMapBoundary = 1.0;
            // shadowMapBoundary = step(currentDepth , 1.0);
            shadowMapBoundary *= step(projCoords.x , 1.0);
            shadowMapBoundary *= step(0.0 , projCoords.x);
            shadowMapBoundary *= step(projCoords.y , 1.0);
            shadowMapBoundary *= step(0.0 , projCoords.y);

            return shadow * shadowMapBoundary;
        }

        void main(){
            vec4 texColor = texture(u_mainTex , v_uv) * u_mainColor;          //纹理颜色

            vec3 L = uboLight._mainLightDir.xyz * -1.0;         //L  指向光源的方向
            vec3 N = normalize(v_wNormal);                      //N  世界法向量(normalize 是必须的,因为片元上的wNormal是差值得到,长度不一定等于1了)
            vec3 V = normalize(uboCam.u_eyePos.xyz - v_wPos.xyz);       //V  指向相机
            vec3 H = normalize(V + L);                          //H  半角向量
            float NoL = dot(N,L);

            //shadow 
            float shadow0 = ShadowCalculation(v_wPosLightSpaces[0] , u_mainShadowMap_0 , NoL);
            float shadow1 = ShadowCalculation(v_wPosLightSpaces[1] , u_mainShadowMap_1 , NoL);
            float shadow2 = ShadowCalculation(v_wPosLightSpaces[2] , u_mainShadowMap_2 , NoL);
            float shadow3 = ShadowCalculation(v_wPosLightSpaces[3] , u_mainShadowMap_3 , NoL);
            float csmSpVal0 = step(0.0 , v_zDLin) * step(v_zDLin , u_csm_split[0]);
            float csmSpVal1 = step(u_csm_split[0] , v_zDLin) * step(v_zDLin , u_csm_split[1]);
            float csmSpVal2 = step(u_csm_split[1] , v_zDLin) * step(v_zDLin , u_csm_split[2]);
            float csmSpVal3 = step(u_csm_split[2] , v_zDLin) * step(v_zDLin , 1.0);
            shadow0 *= csmSpVal0;
            shadow1 *= csmSpVal1;
            shadow2 *= csmSpVal2;
            shadow3 *= csmSpVal3;
            float shadow = shadow0 + shadow1 + shadow2 + shadow3;
            float shadowAtten = 1.0 - shadow;
            vec4 CSMDebugColor = vec4(csmSpVal0 , csmSpVal1 , csmSpVal2 , csmSpVal3);

            //直接光照
            float diffuse = NoL;                                                                //diffuse (漫反射)
            float spec = pow(max(0.0, dot(H , N)) , max(0.01 , uboMaterial.u_spotSpec));        //spec (镜面反射)
            vec3 dirDiffColor = uboLight._mainLightColor.rgb * texColor.xyz * diffuse * shadowAtten;
            vec3 dirSpecColor = uboLight._mainLightColor.rgb * spec * shadowAtten;

            // color = vec4(shadow,0.0,0.0,1.0);
            //color = vec4(1.0,0.0,0.0,1.0);
            //color = vec4(v_uv.x,v_uv.y,0.0,1.0);
            //color = texColor;
            //color = vec4(v_wPos , 1.0);
            //color = vec4(uboLight._mainLightColor , 1.0);
            //color = vec4(uboCam.u_eyePos.xyz , 1.0);
            //color = vec4(L , 1.0);
            //color = vec4(N , 1.0);
            //color = vec4(diffuse,diffuse,diffuse , 1.0);
            //color = vec4(spec,spec,spec , 1.0);
            //color = vec4(dirDiffColor , 1.0);
            //color = vec4(dirSpecColor , 1.0);
            color = vec4(dirDiffColor + dirSpecColor , max (texColor.a , spec * shadowAtten));

            //CSM Debug
            color += CSMDebugColor * 0.2;
         }
        `;
        //使用 屏幕空间阴影图计算阴影
        const vsCode1 = `#version 300 es
        precision mediump float;
        layout (location = 0) in vec4 a_Position;   //顶点 位置 是有默认值的 (0,0,0,1)
        layout (location = 3) in vec2 a_UV;         //UV 纹理坐标
        layout (location = 1) in vec3 a_Normal;     //法线 向量
        uniform ubo_model_block{
            mat4 u_mMtx;                            //模型矩阵
        } uboModel;
        uniform ubo_cam_block{
            mat4 u_viewMtx;                         //视窗矩阵
            mat4 u_projectMtx;                      //投影矩阵
            vec4 u_eyePos;                          //眼睛位置
        } uboCam;

        out vec3 v_wPos;
        out vec2 v_uv;
        out vec3 v_wNormal;
        void main() {
            v_uv = a_UV;
            v_wNormal = mat3(transpose(inverse(uboModel.u_mMtx))) * a_Normal;    //逆转置矩阵,可以让法向量在非统一缩放后保持准确,不受干扰。
            vec4 mPos = vec4(a_Position.xyz,1.0);
            v_wPos = (uboModel.u_mMtx * mPos).xyz;
            // mat4 mvpMat = u_projectMtx * u_viewMtx * uboModel.u_mMtx;
            mat4 mvpMat = uboCam.u_projectMtx * uboCam.u_viewMtx * uboModel.u_mMtx;
            gl_Position = mvpMat * a_Position;
        }
        `;
        const fsCode1 = `#version 300 es
        precision mediump float;
        uniform sampler2D u_mainTex;
        uniform vec4 u_mainColor;           //主颜色 颜色

        uniform ubo_cam_block{
            mat4 u_viewMtx;                     //视窗矩阵
            mat4 u_projectMtx;                  //投影矩阵
            vec4 u_eyePos;                      //眼睛位置
        } uboCam;

        uniform ubo_light_block{
            uniform vec4 _mainLightDir;
            uniform vec4 _mainLightColor;
            uniform vec4[64] _addLightsPos;
            uniform vec4[64] _addLightsColor;
            uniform vec4[64] _addLightsAtt;
            uniform vec4[64] _addLightsSpotDir;
        } uboLight;
        
        uniform ubo_material_block{
            uniform float u_spotSpec;           //镜面反射光斑
        } uboMaterial;

        uniform sampler2D u_screenShadowTex;    //屏幕空间阴影纹理
        uniform vec2 u_resolution;              //画布分辨率

        in vec3 v_wPos;
        in vec2 v_uv;
        in vec3 v_wNormal;
        out vec4 color;

        void main(){
            vec2 screenUV = vec2(gl_FragCoord.x / u_resolution.x , gl_FragCoord.y / u_resolution.y);
            //shadow
            vec4 shadowMapColor = texture(u_screenShadowTex , screenUV);
            float shadowAtten = shadowMapColor.r;
            
            vec4 texColor = texture(u_mainTex , v_uv) * u_mainColor;                    //纹理颜色

            vec3 L = uboLight._mainLightDir.xyz * -1.0;                                             //L  指向光源的方向
            vec3 N = normalize(v_wNormal);                                                          //N  世界法向量(normalize 是必须的,因为片元上的wNormal是差值得到,长度不一定等于1了)
            vec3 V = normalize(uboCam.u_eyePos.xyz - v_wPos.xyz);                                   //V  指向相机
            vec3 H = normalize(V + L);                                                              //H  半角向量
            float NoL = clamp (dot(N,L) , 0. , 1.);


            //直接光照
            float diffuse = NoL;                                                                                              //diffuse (漫反射)
            float spec = pow(max(0.0, dot(H , N)) , max(0.01 , uboMaterial.u_spotSpec)) * step(0.0 , NoL);            //spec (镜面反射) 乘 step(0.0 , NoL) 保证受光面半球才有高光 
            vec3 dirDiffColor = uboLight._mainLightColor.rgb * texColor.xyz * diffuse * shadowAtten;
            vec3 dirSpecColor = uboLight._mainLightColor.rgb * spec * shadowAtten;

            // color = vec4(shadow,0.0,0.0,1.0);
            //color = vec4(1.0,0.0,0.0,1.0);
            //color = vec4(v_uv.x,v_uv.y,0.0,1.0);
            //color = texColor;
            //color = vec4(v_wPos , 1.0);
            //color = vec4(uboLight._mainLightColor , 1.0);
            //color = vec4(uboCam.u_eyePos.xyz , 1.0);
            //color = vec4(L , 1.0);
            // color = vec4(N , 1.0);

            // color = vec4(H * NoL, 1.0);
            // color = vec4(V, 1.0);

            //color = vec4(diffuse,diffuse,diffuse , 1.0);
            //color = vec4(spec,spec,spec , 1.0);
            //color = vec4(dirDiffColor , 1.0);
            //color = vec4(dirSpecColor , 1.0);
            color = vec4(dirDiffColor + dirSpecColor , max (texColor.a , spec * shadowAtten));

         }
        `;
        //
        vsCode0;
        fsCode0;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode1);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode1);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_BUMPED_SPECULAR;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        //
        result.definePropertyMap.u_mainTex = {
            type: "texture",
            value: "White"
        };
        // result.definePropertyMap.u_mainColor = {
        //     type: "color",
        //     value: [1, 1, 1, 1]
        // };
        //block unifrom 属性
        result.definePropertyMap[`ubo_material_block.u_spotSpec`] = {
            type: "float",
            value: 100
        };
        result.definePropertyMap[`u_mainColor`] = {
            type: "color",
            value: [1, 1, 1, 1]
        };
        // result.definePropertyMap[`ubo_light_block.u_lightColor`] = {
        //     type: "color",
        //     value: [1, 1, 1, 1]
        // };
        // result.definePropertyMap[`ubo_light_block.u_lightDir`] = {
        //     type: "float",
        //     value: [0, 0, 0]
        // };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 辐照度卷积 （普通cubemap 渲染到 辐照度的cubemap） 着色器*/
    FIREFLYX.BUILTIN_SHADER_IRRADIANCE_CONVOLUTION = `irradianceConvolution`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_IRRADIANCE_CONVOLUTION] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            
            out vec3 v_wroldPosition;
            mat4 u_projectMtx;                  //投影矩阵,硬编码一个
            mat4 u_viewMtx;                     //视窗矩阵,硬编码一个
            void main() {
                // vec4 pos = vec4(a_Position.xyz , 1.0);
                v_wroldPosition = a_Position.xyz;
                gl_Position = u_projectMtx * u_viewMtx * vec4(WorldPos, 1.0);
            }
        `;
        const fsCode = `#version 300 es
            precision highp float;
            precision highp int;

            uniform samplerCube u_skybox;

            uniform ubo_cam_block{
                mat4 u_viewMtx;                     //视窗矩阵
                mat4 u_projectMtx;                  //投影矩阵
                vec4 u_eyePos;                      //眼睛位置
            } uboCam;

            #define ViewMatrix uboCam.u_viewMtx
            #define ProjectMatrix uboCam.u_projectMtx
            #define SkyBoxTex u_skybox

            float _RadicalInverse_VdC(uint bits) {
                // http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
                // efficient VanDerCorpus calculation.
            
                bits = (bits << 16u) | (bits >> 16u);
                bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
                bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
                bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
                bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
                return float(bits) * 2.3283064365386963e-10; // / 0x100000000
            }
    
            vec2 Hammersley(uint i, uint N) {
                return vec2(float(i) / float(N), _RadicalInverse_VdC(i));
            }

            in vec4 v_position;
            out vec4 color;
            void main(){
                vec3 N = normalize(WorldPos);

                vec3 irradiance = vec3(0.0);
                
                const uint SAMPLE_NUM = 4096u;
                for(uint i=0u;i<SAMPLE_NUM;i++) {
                    vec2 Xi = Hammersley(i, SAMPLE_NUM);
                    vec3 L = CosOnHalfSphere(Xi, N);
                    irradiance += texture(environmentMap, L).xyz;
                }
                
                irradiance *= 1.0 / float(SAMPLE_NUM);
                color = vec4(irradiance , 1.0);
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_IRRADIANCE_CONVOLUTION;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        //
        result.definePropertyMap.u_mainTex = {
            type: "texture",
            value: "white"
        };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 后渲染深度纹理绘制 着色器 */
    FIREFLYX.BUILTIN_SHADER_POSTEFF_DEPTH_TEX = `postEffDepthTex`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_POSTEFF_DEPTH_TEX] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
            out vec2 v_uv;
            void main() {
                v_uv = a_UV;
                vec4 pos = vec4(a_Position.xyz , 1.0);
                gl_Position = pos;
            }
        `;
        const fsCode = `#version 300 es
            precision highp float;
            precision highp int;
            uniform sampler2D u_mainTex;
            uniform float u_depthScale;
            in vec2 v_uv;
            out vec4 color;
            void main(){
                vec4 texColor = texture(u_mainTex , v_uv);    //纹理
                float depthScale = step(1.0 , u_depthScale) * u_depthScale + step(u_depthScale , 1.0) * 1.0;
                float Depth = (1.0 - texColor.r) * depthScale;   //缩放系数是为了方便观察
                // float Depth = 1.0 - texColor.r;
                color = vec4(Depth,0.0,0.0,1.0);
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_POSTEFF_DEPTH_TEX;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        result.definePropertyMap.u_depthScale = {
            type: "float",
            value: 1
        };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 后渲染 一张纹理 着色器 */
    FIREFLYX.BUILTIN_SHADER_POSTEFF_IMAGE = `postEffImage`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_POSTEFF_IMAGE] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
            out vec2 v_uv;
            void main() {
                v_uv = a_UV;
                vec4 pos = vec4(a_Position.xyz , 1.0);
                gl_Position = pos;
            }
        `;
        const fsCode = `#version 300 es
            precision highp float;
            precision highp int;
            uniform sampler2D u_mainTex;
            in vec2 v_uv;
            out vec4 color;
            void main(){
                vec4 texColor = texture(u_mainTex , v_uv);    //纹理
                color = texColor;
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_POSTEFF_IMAGE;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        result.definePropertyMap.u_mainTex = {
            type: "texture",
            value: "White"
        };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 后渲染阴影深度汇集成屏幕空间阴影图 着色器 */
    FIREFLYX.BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW = `postEffScreenShadow`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
            out vec2 v_uv;
            void main() {
                v_uv = a_UV;
                vec4 pos = vec4(a_Position.xyz , 1.0);
                gl_Position = pos;
            }
        `;
        const fsCode = `#version 300 es
            #define SHADOWMAP_TYPE_VSM  

            precision highp float;
            precision highp int;
            uniform sampler2D u_mainTex;
            uniform sampler2D u_mainShadowMap_0;
            uniform sampler2D u_mainShadowMap_1;
            uniform sampler2D u_mainShadowMap_2;
            uniform sampler2D u_mainShadowMap_3;
            uniform mat4 u_lightVPMatrix_0;             //转到 主光源VP空间矩阵
            uniform mat4 u_lightVPMatrix_1;             //转到 主光源VP空间矩阵
            uniform mat4 u_lightVPMatrix_2;             //转到 主光源VP空间矩阵
            uniform mat4 u_lightVPMatrix_3;             //转到 主光源VP空间矩阵
            uniform vec4 u_csm_split;                   //级联阴影拆分数据

            uniform vec2 u_resolution;                  //画布分辨率
            uniform ubo_cam_block{
                mat4 u_viewMtx;             //视窗矩阵
                mat4 u_projectMtx;          //投影矩阵
                vec4 u_eyePos;              //眼睛位置
            } uboCam;

            in vec2 v_uv;
            out vec4 color;

            //diefind val
            #define PI2 6.283185307179586   
            #define SHADOW_SAMPLES 8            //阴影采样次数
            #define SHADOW_BIAS 0.02            //阴影偏移
            #define SHADOW_STRIDE 2.0           //阴影偏移的步幅度
            #define POISSON_RINGS 3             //泊松分布圈层数

            vec2 poissonDisk[SHADOW_SAMPLES];

            //用 uv 作为随机种子 计算随机数
            float random(vec2 uv){
                return fract( sin( dot(uv , vec2(12.9898 , 78.233))) * 43758.5453);
            }

            //泊松圆盘采样分布计算
            void poissonDiskSamples( const in vec2 randomSeed ){
                float ANGLE_STEP = PI2 * float(POISSON_RINGS) / float(SHADOW_SAMPLES);
                float INV_SHADOW_SAMPLES = 1.0 / float( SHADOW_SAMPLES );
                
                float angle = random( randomSeed ) * PI2;
                float radius = INV_SHADOW_SAMPLES;
                float radiusStep = radius;

                for( int i=0 ; i < SHADOW_SAMPLES ; i++ ){
                    poissonDisk[i] = vec2(cos( angle ) , sin( angle ) ) * pow( radius , 0.75 );
                    radius += radiusStep;
                    angle += ANGLE_STEP;
                }
            }

            //计算 PCF 软阴影
            float PCFShadow(vec4 wPosLightSpace , sampler2D shadowMap){
                // 执行透视除法
                vec3 projCoords = wPosLightSpace.xyz / wPosLightSpace.w;
                // 变换到[0,1]的范围
                projCoords = projCoords * 0.5 + 0.5;
                // 取得当前片段在光源视角下的深度
                float currentDepth = projCoords.z;
                
                float stride = SHADOW_STRIDE;                               //定义步长
                float shadowMapSize = float(textureSize(shadowMap, 0));     //shadowmap分辨率
                float visibility = 0.0;                                     //初始可见项
                float filterRange = stride / shadowMapSize;                 //滤波窗口的范围

                //泊松圆盘采样得到采样点
                poissonDiskSamples(projCoords.xy);

                //对每个点进行比较深度值并累加
                for(int i = 0; i < SHADOW_SAMPLES; i++){
                    float shadow_depth = texture(shadowMap , projCoords.xy + poissonDisk[i] * filterRange).r;
                    // float res = (currentDepth < shadow_depth) ? 1.0 : 0.0;
                    // float res = (currentDepth < shadow_depth) ? 0.0 : 1.0;
                    visibility += step(shadow_depth , currentDepth - SHADOW_BIAS);
                }

                //返回均值
                float avgVisibility = visibility / float(SHADOW_SAMPLES);
                return avgVisibility;
            }

            vec2 unpackRGBATo2Half(vec4 v) {
                return vec2(v.x + (v.y / 255.0), v.z + (v.w / 255.0));
            }
            
            vec2 texture2DDistribution(sampler2D shadow, vec2 uv) {
                return unpackRGBATo2Half(texture(shadow, uv));
            }

            //获取 VSM
            float VSMShadow(sampler2D shadow, vec2 uv, float compare) {
                float occlusion = 1.0;
                vec2 distribution = texture2DDistribution(shadow, uv);
                // vec2 distribution = texture2D(shadow, uv);
                float hard_shadow = step(compare, distribution.x);
                if(hard_shadow != 1.0) {
                    float distance = compare - distribution.x;
                    float variance = max(0.00000, distribution.y * distribution.y);
                    float softness_probability = variance / (variance + distance * distance);
                    softness_probability = clamp((softness_probability - 0.3) / (0.95 - 0.3), 0.0, 1.0);
                    occlusion = clamp(max(hard_shadow, softness_probability), 0.0, 1.0);
                }
                return 1.0 - occlusion;
            }

            //计算阴影
            float ShadowCalculation(vec4 wPosLightSpace , sampler2D shadowMap , float NoL)
            {
                // 执行透视除法
                vec3 projCoords = wPosLightSpace.xyz / wPosLightSpace.w;
                // 变换到[0,1]的范围
                projCoords = projCoords * 0.5 + 0.5;
                //调节 阴影失真(Shadow Acne) 问题使用 阴影偏移
                float baseBias = SHADOW_BIAS;
                // float bias = max(baseBias * (1.0 - NoL), baseBias * 0.1);  //自适应处理
                float bias = baseBias; 
                // 取得当前片段在光源视角下的深度
                float currentDepth = projCoords.z - bias;
                
                bool isSoft = true;
                
                float shadow = 0.0;
                if(!isSoft){
                    // 取得最近点的深度(使用[0,1]范围下的wPosLightSpace当坐标)
                    float closestDepth = texture(shadowMap, projCoords.xy).r;
                    // 检查当前片段是否在阴影中
                    shadow = step(closestDepth , currentDepth);
                }else{
                    #ifdef SHADOWMAP_TYPE_VSM
                        shadow = VSMShadow(shadowMap , projCoords.xy , currentDepth);
                    #else
                        shadow = PCFShadow(wPosLightSpace , shadowMap);
                    #endif
                }

                //超出 置 0处理 ,解决黑影错误问题
                float shadowMapBoundary = 1.0;
                // shadowMapBoundary = step(currentDepth , 1.0);
                shadowMapBoundary *= step(projCoords.x , 1.0);
                shadowMapBoundary *= step(0.0 , projCoords.x);
                shadowMapBoundary *= step(projCoords.y , 1.0);
                shadowMapBoundary *= step(0.0 , projCoords.y);

                return shadow * shadowMapBoundary;
            }

            void main(){
                mat4 inverseVP = inverse(uboCam.u_projectMtx * uboCam.u_viewMtx);
                vec4 texColor = texture(u_mainTex , v_uv);      //深度纹理
                vec3 screenPosN = vec3(gl_FragCoord.x / u_resolution.x , gl_FragCoord.y / u_resolution.y , texColor.r);
                vec4 ndcPos = vec4(screenPosN * 2.0 - 1.0 , 1.0);
                //VP逆矩阵将NDC坐标转换到世界空间
                vec4 wPos = inverseVP * ndcPos;
                wPos = wPos / wPos.w;                           //将齐次坐标w分量变1得到世界坐标
                wPos.w = 1.0;
                // float Depth = (1.0 - texColor.r) * 500.;     //500 缩放系数是为了方便观察
                // float Depth = 1.0 - texColor.r;
                // color = vec4(Depth,0.0,0.0,1.0);
                // color = vec4(ndcPos.rgb,1.0);                //NDC pos
                // color = vec4(wPos.xyz,1.0);                  //view Pos

                //view 空间坐标
                vec4 vPos = uboCam.u_viewMtx * wPos;
                //级联锥分段值
                float shadowDistance = u_csm_split[3];
                float zDLin = vPos.z / shadowDistance;       
                //计算光源空间世界坐标
                vec4 v_wPosLightSpaces_0 = u_lightVPMatrix_0 * wPos;
                vec4 v_wPosLightSpaces_1 = u_lightVPMatrix_1 * wPos;
                vec4 v_wPosLightSpaces_2 = u_lightVPMatrix_2 * wPos;
                vec4 v_wPosLightSpaces_3 = u_lightVPMatrix_3 * wPos;

                //shadow
                float shadow0 = ShadowCalculation(v_wPosLightSpaces_0 , u_mainShadowMap_0 , 0.0);
                float shadow1 = ShadowCalculation(v_wPosLightSpaces_1 , u_mainShadowMap_1 , 0.0);
                float shadow2 = ShadowCalculation(v_wPosLightSpaces_2 , u_mainShadowMap_2 , 0.0);
                float shadow3 = ShadowCalculation(v_wPosLightSpaces_3 , u_mainShadowMap_3 , 0.0);
                //级联混合
                float csmSpVal0 = step(0.0 , zDLin) * step(zDLin , u_csm_split[0]);
                float csmSpVal1 = step(u_csm_split[0] , zDLin) * step(zDLin , u_csm_split[1]);
                float csmSpVal2 = step(u_csm_split[1] , zDLin) * step(zDLin , u_csm_split[2]);
                float csmSpVal3 = step(u_csm_split[2] , zDLin) * step(zDLin , 1.0);
                shadow0 *= csmSpVal0;
                shadow1 *= csmSpVal1;
                shadow2 *= csmSpVal2;
                shadow3 *= csmSpVal3;
                float shadow = shadow0 + shadow1 + shadow2 + shadow3;
                float shadowAtten = 1.0 - shadow;

                color = vec4(shadowAtten,shadowAtten,shadowAtten,1.0);   //屏幕空间阴影输出
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 简单线条绘制 着色器 */
    FIREFLYX.BUILTIN_SHADER_SIMPLE_LINE = `simpleLine`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_SIMPLE_LINE] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            uniform ubo_cam_block{
                mat4 u_viewMtx;             //视窗矩阵
                mat4 u_projectMtx;          //投影矩阵
                vec4 u_eyePos;              //眼睛位置
            } uboCam;
            void main() {
                mat4 vpMat = uboCam.u_projectMtx * uboCam.u_viewMtx;    //顶点数据已经是世界坐标,不需要M矩阵.
                gl_Position = vpMat * vec4(a_Position.xyz , 1.0);
            }
        `;
        const fsCode = `#version 300 es
            precision highp float;
            precision highp int;
            uniform vec4 u_mainColor;       //主颜色 颜色
            out vec4 color;
            void main(){
                color = u_mainColor;
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_SIMPLE_LINE;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        result.definePropertyMap["u_mainColor"] = {
            type: "color",
            value: [1, 0, 1, 1]
        };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 后渲染 一张纹理 着色器 */
    FIREFLYX.BUILTIN_SHADER_SKY_BOX = `skyBox`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_SKY_BOX] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            
            out vec4 v_position;
            void main() {
                // vec4 pos = vec4(a_Position.xyz , 1.0);
                v_position = a_Position;
                gl_Position = a_Position;
                gl_Position.z = 0.9999;    //深度设置为最大
            }
        `;
        const fsCode = `#version 300 es
            precision highp float;
            precision highp int;

            uniform samplerCube u_skybox;

            uniform ubo_cam_block{
                mat4 u_viewMtx;                     //视窗矩阵
                mat4 u_projectMtx;                  //投影矩阵
                vec4 u_eyePos;                      //眼睛位置
            } uboCam;

            #define ViewMatrix uboCam.u_viewMtx
            #define ProjectMatrix uboCam.u_projectMtx
            #define SkyBoxTex u_skybox

            in vec4 v_position;
            out vec4 color;
            void main(){
                mat4 viewDirction = mat4(ViewMatrix); 
                // 我们只关心方向所以清除移动的部分
                viewDirction[3][0] = 0.0;
                viewDirction[3][1] = 0.0;   
                viewDirction[3][2] = 0.0;

                mat4 viewDirectionProjectionInverse = inverse(ProjectMatrix * viewDirction);
                vec4 t = viewDirectionProjectionInverse * v_position;
                vec4 texColor = texture(SkyBoxTex , (t.xyz / t.w));    //除以w转换4D到3D
                color = texColor;
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_SKY_BOX;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        // result.definePropertyMap.u_mainTex = {
        //     type: "texture",
        //     value: "White"
        // };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 标准 PBR 着色器*/
    FIREFLYX.BUILTIN_SHADER_STANDARD = `standard`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_STANDARD] = () => {
        const vsCode = `#version 300 es
        precision mediump float;
        //顶点、UV、法线、切线
        layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
        layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
        layout (location = 1) in vec3 a_Normal;   //法线 向量
        layout (location = 2) in vec4 a_Tangent;  //切线 向量
        uniform ubo_model_block{
            mat4 u_mMtx;                //模型矩阵
        } uboModel;
        uniform ubo_cam_block{
            mat4 u_viewMtx;             //视窗矩阵
            mat4 u_projectMtx;          //投影矩阵
            vec4 u_eyePos;              //眼睛位置
        } uboCam;
        
        out vec3 v_wPos;
        out vec2 v_uv;
        out vec3 v_wNormal;
        out mat3 v_TBN_Mtx;

        //构建 世界空间下 的 TBN 空间矩阵
        //it_m_mtx 转到世界间 逆转置 矩阵 (魔法矩阵)
        //_normal 顶点法线向量
        //_tangent 顶点切线向量
        mat3 MakeTBNMatrix(mat3 it_m_mtx,lowp vec3 _normal,lowp vec4 _tangent)
        {
            vec3 normal = normalize(it_m_mtx * _normal);
            vec3 tangent = normalize(it_m_mtx * _tangent.xyz);
            vec3 binormal = cross(normal , tangent) * _tangent.w;
            return mat3(tangent , binormal , normal);
        }

        void main() {
            v_uv = a_UV;
            mat3 it_mMtx = mat3(transpose(inverse(uboModel.u_mMtx)));
            v_wNormal = it_mMtx * a_Normal;         //逆转置矩阵,可以让法向量在非统一缩放后保持准确,不受干扰。
            vec4 mPos = vec4(a_Position.xyz,1.0);
            v_wPos = (uboModel.u_mMtx * mPos).xyz;
            //TBN 空间矩阵
            v_TBN_Mtx = MakeTBNMatrix(it_mMtx , a_Normal , a_Tangent);
            // mat4 mvpMat = u_projectMtx * u_viewMtx * uboModel.u_mMtx;
            mat4 mvpMat = uboCam.u_projectMtx * uboCam.u_viewMtx * uboModel.u_mMtx;
            gl_Position = mvpMat * a_Position;
        }
        `;
        const fsCode = `#version 300 es
        precision mediump float;
        uniform sampler2D u_mainTex;        
        uniform vec4 u_mainColor;           //主颜色 颜色
        uniform sampler2D u_metaRougTex;    //金属粗糙度纹理
        uniform sampler2D u_normalTex;      //法线纹理
        uniform samplerCube u_skybox;       //环境漫反射 纹理

        uniform ubo_cam_block{
            mat4 u_viewMtx;                     //视窗矩阵
            mat4 u_projectMtx;                  //投影矩阵
            vec4 u_eyePos;                      //眼睛位置
        } uboCam;

        uniform ubo_light_block{
            uniform vec4 _mainLightDir;
            uniform vec4 _mainLightColor;
            uniform vec4[64] _addLightsPos;
            uniform vec4[64] _addLightsColor;
            uniform vec4[64] _addLightsAtt;
            uniform vec4[64] _addLightsSpotDir;
        } uboLight;

        in vec3 v_wPos;
        in vec2 v_uv;
        in vec3 v_wNormal;
        in mat3 v_TBN_Mtx;

        out vec4 color;

        #define PI 3.1415926
        #define Nonmetal_F0 vec3(0.04,0.04,0.04)

        vec3 UnpackNormal(vec4 nTexRGB){
            return nTexRGB.rgb * 2. - 1.;
        }

        float saturate(float x){
            return clamp(x, 0.0, 1.0);
        }

        float chiGGX(float v)
        {
            return v > 0. ? 1. : 0.;
        }

        float GGX_PartialGeometryTerm(float alpha , float _oN , float _oH){
            float VoH2 = saturate(_oN);
            float chi = chiGGX( VoH2 / saturate(_oH) );
            VoH2 = VoH2 * VoH2;
            float tan2 = ( 1. - VoH2 ) / VoH2;
            return (chi * 2.) / ( 1. + sqrt( 1. + alpha * alpha * tan2 ) );
        }

        float G_GGX(float _Roughness , float NoV , float NoL , float VoH, float LoH){
            float g = GGX_PartialGeometryTerm(_Roughness,NoV,VoH) * GGX_PartialGeometryTerm(_Roughness,NoL,LoH);
            return max(0.0001 , g);
        }

        // Fresnel [1994] Schlick
        vec3 Fresnel_Schlick(float cosT, vec3 F0)
        {
            return F0 + (1. - F0) * pow(1. - cosT, 5.);
        }

        // GGX [2007] Trowbridge-Reitz
        float UE_D_GGX(float Roughness , float NoH){
            float a = Roughness * Roughness;
            float a2 = a * a;
            float d = (NoH * a2 - NoH) * NoH + 1.0;
            // return a2 / max(HALF_EPS , PI * d * d);
            return (a2 / (PI * d * d));
        }

        //indirect diff Fresnel Schlick , c to I_Kd
        vec3 I_D_Fresnel_Schlick(float cosT, vec3 F0 , float roughness)
        {
            float _r = 1. - roughness;
            vec3 _A = max(vec3(_r,_r,_r) , F0);
            return F0 + (_A - F0) * pow(1. - cosT, 5.);
        }

        //直接光 镜面反射
        vec3 Direct_BRDF_Specular(float _Roug , float _Met , float NoV , float NoL , float NoH , float VoH , float LoH , vec3 F0, out vec3 D_ks){
            //直接光 spec , BRDF f(l,v) = D*F*G / 配平值

            //      D (h), GGX[2007]
            // half a2 = Pow4(_Roug);
            float D = UE_D_GGX(_Roug , NoH);

            //      F (v,h), Schlick[1994]
            // vec3 F = Fresnel_Schlick(saturate(VoH),_F0.xyz);
            vec3 F = Fresnel_Schlick(saturate(VoH), F0);
            // vec3 F = UE_F_Fresnel(_F0,VoH);

            //      G (l,v,h), Schlick-GGX
            // float G = UE_G_Vis_Schlick(_Roug , saturate(NoV) , saturate(NoL));
            // float G = UE_Vis_smithJointApprox(_Roug , NoV , NoL);
            float G = G_GGX(_Roug , NoV , NoL , VoH , LoH);

            //      配平值 4*(n.l)(n.v)
            float S_denominator = 4. * NoL * NoV;


            D_ks = F;
            // return D * G * F / S_denominator;
            return D * G * F ;
        }

        //间接光
        vec3 Indirect_BRDF(float _Roug , float _Met, vec3 ALBEDO, vec3 N ,vec3 R , float NoV,vec3 F0){
            //间接光 diff ,cubeMap 模糊(mip)
            // vec3 I_Diff = SAMPLE_TEXTURECUBE_LOD(_EvnMap, sampler_EvnMap, R, 10).rgb;
            vec3 I_F = I_D_Fresnel_Schlick(saturate(NoV), F0 ,_Roug);
            // vec3 I_F = I_D_Fresnel_Schlick(saturate(NoV), vec3(0.04,0.04,0.04) ,_Roug);
            vec3 I_kd = (1. - I_F) * (1. - _Met);
            // vec3 irradiance = SAMPLE_TEXTURECUBE_LOD(_EvnMap, sampler_EvnMap, N, mip).rgb;
            vec3 irradiance = textureLod(u_skybox, N, 7.).rgb;
            // vec3 I_Diff = irradiance/PI * ALBEDO;  //
            vec3 I_Diff = irradiance * ALBEDO;

            // //间接光 spec ,IBL
            // // vec3 I_ks = 0;
            // // vec3 I_kd = (1 - I_ks) * (1 - _Met);
            // vec3 I_S_BRDF_I = SAMPLE_TEXTURE2D(_BRDFLUT, sampler_BRDFLUT, vec2(saturate(NoV),_Roug)).rgb;
            // vec3 reflection = prefilteredReflection(R, _Roug).rgb;	
            // vec3 I_Specular =  reflection * (I_F * I_S_BRDF_I.x + I_S_BRDF_I.y);
            
            // vec3 I_Color = I_Diff * I_kd + I_Specular;
            vec3 I_Color = I_Diff ;
            return I_Color;
        }

        void main(){
            vec4 texColor = texture(u_mainTex , v_uv) * u_mainColor;                    //纹理颜色
            vec4 texMetRoug = texture(u_metaRougTex , v_uv);                            //金属度+粗糙度 纹理
            vec3 ALBEDO = texColor.rgb;
            vec4 texNormal = texture(u_normalTex , v_uv);                               //法线纹理


            vec3 L = uboLight._mainLightDir.xyz * -1.0;                                             //L  指向光源的方向
            // vec3 N = normalize(v_wNormal);                                                       //N  世界法向量(normalize 是必须的,因为片元上的wNormal是差值得到,长度不一定等于1了)
            vec3 N = normalize(v_TBN_Mtx * UnpackNormal(texNormal));                                //N  世界法向量
            vec3 V = normalize(uboCam.u_eyePos.xyz - v_wPos.xyz);                                   //V  指向相机
            vec3 H = normalize(V + L);
            vec3 R = reflect(-V,N);                                                              //H  半角向量
            float NoL = dot(N,L);
            float NoV = dot(N,V);
            // float NoH = (dot(N,H));
            // float VoH = (dot(V,H));
            // float LoH = (dot(L,H));
            float NoH = saturate(dot(N,H));
            float VoH = saturate(dot(V,H));
            float LoH = saturate(dot(L,H));

            
            float _Met = 0.5 * texMetRoug.r;
            float _Roug = 0.9 * texMetRoug.g;
            vec3 F0 = mix(Nonmetal_F0 , ALBEDO , _Met);
            vec3 D_ks = vec3(0.);

            //--------------------直接光照----------------------------
            //  Blinn-Phong 镜面反射
            float spec = pow(max(0.0, dot(H , N)) , max(0.01 , 100.)) * step(0.0 , NoL);            //Blinn-Phong 的 spec (镜面反射) 乘 step(0.0 , NoL) 保证受光面半球才有高光 
            vec3 dirSpecColor = uboLight._mainLightColor.rgb * spec ;
            //  GGX 镜面反射
            dirSpecColor = Direct_BRDF_Specular(_Roug , _Met , NoV , NoL , NoH , VoH , LoH , F0, D_ks);

            //  漫反射
            float diffuse = NoL;                                                                                              //diffuse (漫反射)
            vec3 dirDiffColor = uboLight._mainLightColor.rgb * texColor.xyz * diffuse ;
            vec3 D_kd = (1. - D_ks) * (1. - _Met);
            dirDiffColor *= D_kd;

            vec3 dirLightContribute = dirDiffColor + dirSpecColor;
            //--------------------------------------------------------

            //--------------------间接光照----------------------------

            // vec3 indirectContribute = vec3(0,0,0);
            vec3 indirectContribute = Indirect_BRDF(_Roug , _Met , ALBEDO,  N , R ,  NoV, F0);
            //--------------------------------------------------------
            
            // vec3 dirLightContribute = dirDiffColor;
            // vec3 dirLightContribute = dirSpecColor;

            // color = vec4(shadow,0.0,0.0,1.0);
            //color = vec4(1.0,0.0,0.0,1.0);
            //color = vec4(v_uv.x,v_uv.y,0.0,1.0);
            //color = texColor;
            //color = vec4(v_wPos , 1.0);
            //color = vec4(uboLight._mainLightColor , 1.0);
            //color = vec4(uboCam.u_eyePos.xyz , 1.0);
            //color = vec4(L , 1.0);
            // color = vec4(N , 1.0);

            // color = vec4(H * NoL, 1.0);
            // color = vec4(V, 1.0);

            //color = vec4(diffuse,diffuse,diffuse , 1.0);
            //color = vec4(spec,spec,spec , 1.0);
            //color = vec4(dirDiffColor , 1.0);
            //color = vec4(dirSpecColor , 1.0);
            // color = vec4(texNormal.rgb , 1.0);

            color = vec4(dirLightContribute + indirectContribute, max (texColor.a , spec));
         }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_STANDARD;
        result.lightMode = FIREFLYX.RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        //
        result.definePropertyMap.u_mainTex = {
            type: "texture",
            value: "white"
        };
        //
        result.definePropertyMap.u_metaRougTex = {
            type: "texture",
            value: "white"
        };
        //法线纹理
        result.definePropertyMap.u_normalTex = {
            type: "texture",
            value: "normal"
        };
        //
        result.definePropertyMap.u_mainColor = {
            type: "color",
            value: [1, 1, 1, 1]
        };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
var FIREFLYX;
(function (FIREFLYX) {
    /** 方差阴影图 着色器*/
    FIREFLYX.BUILTIN_SHADER_VSM = `VarianceShadowMap`;
    FIREFLYX.DefaultAsset.shaderGenMap[FIREFLYX.BUILTIN_SHADER_VSM] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
            out vec2 v_uv;
            void main() {
                v_uv = a_UV;
                vec4 pos = vec4(a_Position.xyz , 1.0);
                gl_Position = pos;
            }
        `;
        const fsCode = `#version 300 es
            precision mediump float;

            uniform sampler2D u_mainTex;
            uniform vec2 u_resolution;              //画布分辨率
            uniform float u_radius;                 //采样半径
            uniform float u_samples;                //采样次数
            uniform float u_horizontalPass;         //是否水平方向
            
            in vec2 v_uv;
            out vec4 color;

            vec4 pack2HalfToRGBA( vec2 v ) {
                vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );
                return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );
            }
            vec2 unpackRGBATo2Half( vec4 v ) {
                return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );
            }

            void main(){
                //vsm calc
                float samples = u_samples;
                float mean = 0.0;
                float squared_mean = 0.0;
                float uvStride = samples <= 1.0 ? 0.0 : 2.0 / (samples - 1.0);
                float uvStart = samples <= 1.0 ? 0.0 : -1.0;
                for(float i = 0.0; i < samples; i++) {
                    float uvOffset = uvStart + i * uvStride;
                    if(u_horizontalPass == 1.0){
                        //水平方向计算
                        vec2 distribution = unpackRGBATo2Half( texture( u_mainTex, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * u_radius ) / u_resolution) );
                        mean += distribution.x;
                        squared_mean += distribution.y * distribution.y + distribution.x * distribution.x;
                    }else{
                        //垂直方向计算
                        float depth = texture(u_mainTex, (gl_FragCoord.xy + vec2(0.0, uvOffset) * u_radius) / u_resolution).r;
                        mean += depth;
                        squared_mean += depth * depth;
                    }
                }
                mean = mean / samples;
                squared_mean = squared_mean / samples;
                float std_dev = sqrt(squared_mean - mean * mean);
                color = pack2HalfToRGBA(vec2(mean, std_dev));
            }
        `;
        //glsl
        const vs = new FIREFLYX.GLSL();
        vs.setCode(vsCode);
        const fs = new FIREFLYX.GLSL();
        fs.setCode(fsCode);
        //shader
        let result = new FIREFLYX.Shader();
        result.tag = FIREFLYX.BUILTIN_SHADER_VSM;
        result.lightMode = FIREFLYX.RenderPipeline.LM_SHADOW_CASTER;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};
        result.definePropertyMap.u_radius = {
            type: "float",
            value: 1,
        };
        //apply
        result.apply();
        return result;
    };
})(FIREFLYX || (FIREFLYX = {}));
